# Zapini API - Documentação Completa

**Versão:** 1.2.0
**Base URL:** `https://zapini.app/api/v1`

> **Breaking Changes v1.2.0:** IDs de conversas, tags, agendamentos e integrações agora são UUIDs (strings) em vez de inteiros.

---

## Introdução

A API do Zapini permite integrar seu sistema com a plataforma de mensagens WhatsApp. Com ela, você pode enviar mensagens, gerenciar conversas, contatos e muito mais.

### Características Principais

- **RESTful API** com respostas em JSON
- **Autenticação** via Bearer Token (Sanctum)
- **Rate Limiting** para proteção contra abusos
- **Webhooks** para receber eventos em tempo real
- **Suporte a mídia** (imagens, vídeos, áudios, documentos)

---

## Autenticação

A API suporta dois métodos de autenticação:

### 1. Tokens de Usuário (Sanctum)
Após fazer login via `/auth/login`, você recebe um token de usuário para acesso completo à plataforma.

### 2. Tokens de API da Instância (Recomendado para Integrações)
Gere tokens de API (prefixo `sk_`) para instâncias específicas do WhatsApp. Esses tokens:
- Não exigem login de usuário
- São limitados a uma única instância
- Podem ser revogados independentemente
- Funcionam com todos os endpoints da API

### Headers Obrigatórios

```
Authorization: Bearer {seu_token}
Accept: application/json
Content-Type: application/json
```

**Exemplo com Token de API:**
```bash
curl -X GET https://zapini.app/api/v1/tags \
  -H "Authorization: Bearer sk_abc123..." \
  -H "Accept: application/json"
```

---

## URLs Base

A API pode ser acessada por duas URLs:

### URL Principal da API
```
https://zapini.app/api/v1
```
Acesse todos os endpoints pela URL principal da plataforma.

### URL do Subdomínio da Instância (Gateway Unificado)
```
https://instance-{id}.zapini.app
```
Cada instância do WhatsApp tem seu próprio subdomínio que funciona como um **gateway de API unificado**:
- Operações diretas do WhatsApp (mais rápidas, sem proxy)
- Todos os endpoints da plataforma (proxy automático)

**Benefícios do Subdomínio da Instância:**
- Menor latência para operações do WhatsApp
- Uma única URL base para todas as operações
- O mesmo token `sk_` funciona para tudo

**Exemplo:**
```bash
# Status direto do WhatsApp (rápido)
curl https://instance-18.zapini.app/status \
  -H "Authorization: Bearer sk_abc123..."

# API da plataforma (via proxy)
curl https://instance-18.zapini.app/api/v1/tags \
  -H "Authorization: Bearer sk_abc123..."
```

---

## Formato das Respostas

### Resposta de Sucesso

```json
{
  "success": true,
  "message": "Operação realizada com sucesso",
  "data": {
    // dados da resposta
  }
}
```

### Resposta com Paginação

```json
{
  "success": true,
  "data": [...],
  "meta": {
    "pagination": {
      "total": 100,
      "per_page": 20,
      "current_page": 1,
      "last_page": 5,
      "from": 1,
      "to": 20
    }
  }
}
```

### Resposta de Erro

```json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Descrição do erro",
    "details": {}
  }
}
```

---

## Códigos de Erro HTTP

| Código | Descrição |
|--------|-----------|
| 200 | Sucesso |
| 201 | Recurso criado |
| 400 | Requisição inválida |
| 401 | Não autorizado |
| 403 | Acesso proibido |
| 404 | Recurso não encontrado |
| 409 | Conflito (recurso já existe) |
| 422 | Erro de validação |
| 429 | Rate limit excedido / Cota excedida |
| 500 | Erro interno do servidor |

---

## Limites de Uso

- **Rate Limit:** 100 requisições por 15 minutos por IP
- **Mensagens WhatsApp:** 500 mensagens por dia por instância
- **Upload de arquivos:**
  - Imagens: 16 MB
  - Vídeos: 64 MB
  - Áudios: 16 MB
  - Documentos: 100 MB

---

## Índice de Endpoints

1. [Autenticação](#autenticacao-endpoints)
2. [Instâncias WhatsApp](#instancias-whatsapp)
3. [Mensagens](#mensagens)
4. [Conversas](#conversas)
5. [Controle de Automação](#controle-de-automação)
6. [Tags](#tags)
7. [Contatos](#contatos)
8. [Agendamentos](#agendamentos)
9. [Mídia](#midia)
10. [Chat API](#chat-api-otimizada-para-ui)

---

## Autenticação Endpoints

### POST /auth/login

Realiza login e retorna o token de acesso.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| email | string | Sim | Email do usuário |
| password | string | Sim | Senha do usuário |
| device_name | string | Não | Nome do dispositivo |

**Exemplo de Requisição:**

```bash
curl -X POST https://zapini.app/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "email": "usuario@exemplo.com",
    "password": "sua_senha",
    "device_name": "Meu App"
  }'
```

**Resposta de Sucesso:**

```json
{
  "success": true,
  "message": "Login realizado com sucesso",
  "data": {
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Usuário",
      "email": "usuario@exemplo.com",
      "role": "admin",
      "locale": "pt-BR",
      "tenant": {
        "id": "550e8400-e29b-41d4-a716-446655440001",
        "name": "Minha Empresa",
        "status": "active"
      }
    },
    "token": "1|abc123xyz...",
    "token_type": "Bearer",
    "expires_at": null
  }
}
```

**Resposta com 2FA Necessário:**

```json
{
  "success": true,
  "two_factor_required": true,
  "message": "Verificação de dois fatores necessária"
}
```

---

### POST /auth/two-factor

Completa o login com código 2FA.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| email | string | Sim | Email do usuário |
| password | string | Sim | Senha do usuário |
| code | string | Sim | Código 2FA (6 dígitos) |
| device_name | string | Não | Nome do dispositivo |

---

### POST /auth/register

Registra um novo usuário.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| name | string | Sim | Nome completo |
| email | string | Sim | Email (único) |
| password | string | Sim | Senha (mín. 8 caracteres) |
| password_confirmation | string | Sim | Confirmação da senha |
| device_name | string | Não | Nome do dispositivo |
| terms_accepted | boolean | Sim | Aceite dos termos |

---

### GET /auth/me

Retorna os dados do usuário autenticado.

**Headers:** Requer autenticação

**Resposta:**

```json
{
  "success": true,
  "data": {
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Usuário",
      "email": "usuario@exemplo.com",
      "role": "admin",
      "locale": "pt-BR",
      "timezone": "America/Sao_Paulo",
      "email_verified": true,
      "two_factor_enabled": false,
      "tenant": {
        "id": "550e8400-e29b-41d4-a716-446655440001",
        "name": "Minha Empresa",
        "status": "active"
      }
    }
  }
}
```

---

### PATCH /auth/me

Atualiza o perfil do usuário.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| name | string | Não | Novo nome |
| locale | string | Não | Idioma (en, pt-BR, es) |
| timezone | string | Não | Fuso horário |

---

### POST /auth/logout

Encerra a sessão atual (revoga o token).

---

### POST /auth/logout-all

Encerra todas as sessões (revoga todos os tokens).

---

### POST /auth/refresh

Renova o token de acesso.

**Resposta:**

```json
{
  "success": true,
  "message": "Token renovado",
  "data": {
    "token": "2|novo_token...",
    "token_type": "Bearer",
    "expires_at": null
  }
}
```

---

### POST /auth/password/forgot

Envia email de recuperação de senha.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| email | string | Sim | Email do usuário |

---

### POST /auth/password/reset

Redefine a senha com token de recuperação.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| token | string | Sim | Token de recuperação |
| email | string | Sim | Email do usuário |
| password | string | Sim | Nova senha |
| password_confirmation | string | Sim | Confirmação |

---

### POST /auth/password/change

Altera a senha (usuário autenticado).

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| current_password | string | Sim | Senha atual |
| password | string | Sim | Nova senha |
| password_confirmation | string | Sim | Confirmação |

---

### GET /auth/sessions

Lista todas as sessões ativas do usuário.

**Resposta:**

```json
{
  "success": true,
  "data": {
    "sessions": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440001",
        "name": "Chrome - Windows",
        "last_used_at": "2025-01-15T10:30:00Z",
        "created_at": "2025-01-10T08:00:00Z",
        "is_current": true
      }
    ]
  }
}
```

---

### DELETE /auth/sessions/{id}

Revoga uma sessão específica.

---

## Instâncias WhatsApp

### GET /instances

Lista todas as instâncias WhatsApp.

**Parâmetros Query:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| status | string | Filtrar por status (connected, disconnected, qr_ready, pending) |

**Resposta:**

```json
{
  "success": true,
  "data": {
    "instances": [
      {
        "uuid": "550e8400-e29b-41d4-a716-446655440000",
        "phone_number": "5511999999999",
        "status": "connected",
        "connected": true,
        "messages_sent_today": 50,
        "remaining_quota": 450,
        "can_send_messages": true,
        "last_activity_at": "2025-01-15T10:30:00Z",
        "created_at": "2025-01-01T00:00:00Z"
      }
    ]
  }
}
```

---

### GET /instances/{uuid}

Retorna detalhes de uma instância.

**Resposta:**

```json
{
  "success": true,
  "data": {
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "phone_number": "5511999999999",
    "status": "connected",
    "connected": true,
    "messages_sent_today": 50,
    "remaining_quota": 450,
    "can_send_messages": true,
    "daily_limit": 500,
    "failed_attempts": 0,
    "port": 3000,
    "server": {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "name": "Server 01",
      "status": "active"
    }
  }
}
```

---

### GET /instances/{uuid}/qr

Obtém o QR Code para conexão.

**Resposta:**

```json
{
  "success": true,
  "data": {
    "status": "qr_ready",
    "qr_code": "data:image/png;base64,...",
    "qr_code_text": "2@...",
    "qr_generated_at": "2025-01-15T10:30:00Z",
    "qr_expired": false,
    "message": "QR code pronto para escanear"
  }
}
```

---

### GET /instances/{uuid}/status

Retorna o status de conexão da instância.

**Parâmetros Query:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| realtime | boolean | Obter status em tempo real do servidor |

**Resposta:**

```json
{
  "success": true,
  "data": {
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "phone_number": "5511999999999",
    "status": "connected",
    "connected": true,
    "messages_sent_today": 50,
    "remaining_quota": 450,
    "daily_limit": 500,
    "can_send_messages": true,
    "last_activity_at": "2025-01-15T10:30:00Z"
  }
}
```

---

### GET /instances/{uuid}/stats

Retorna estatísticas da instância.

**Resposta:**

```json
{
  "success": true,
  "data": {
    "messages_sent_today": 50,
    "remaining_quota": 450,
    "daily_limit": 500,
    "messages_this_week": 200,
    "messages_this_month": 800,
    "messages_received_today": 30,
    "conversations_count": 150,
    "contacts_count": 500,
    "failed_attempts": 0,
    "last_activity_at": "2025-01-15T10:30:00Z"
  }
}
```

---

### POST /instances/{uuid}/disconnect

Desconecta a instância do WhatsApp.

---

### POST /instances/{uuid}/reconnect

Inicia o processo de reconexão.

---

## Endpoints Diretos do WhatsApp (Subdomínio da Instância)

Esses endpoints estão disponíveis **apenas** via URL do subdomínio da instância (`https://instance-{id}.zapini.app`). Eles fornecem acesso direto às operações do WhatsApp com menor latência.

### GET /status

Obtém o status de conexão em tempo real.

**Exemplo:**
```bash
curl https://instance-18.zapini.app/status \
  -H "Authorization: Bearer sk_abc123..."
```

**Resposta:**
```json
{
  "success": true,
  "status": "connected",
  "connected": true,
  "connecting": false,
  "uptime": 3600.5,
  "phoneNumber": "5511999999999",
  "lastHeartbeat": "2025-01-15T10:30:00Z"
}
```

---

### GET /qr

Obtém o QR code para autenticação.

**Resposta:**
```json
{
  "success": true,
  "status": "qr_ready",
  "qr_code": "data:image/png;base64,...",
  "qr_code_text": "2@abc..."
}
```

---

### POST /send-message

Envia uma mensagem de texto diretamente via WhatsApp.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| number | string | Sim | Número do destinatário (ex: 5511999999999) |
| message | string | Sim | Conteúdo da mensagem |

**Exemplo:**
```bash
curl -X POST https://instance-18.zapini.app/send-message \
  -H "Authorization: Bearer sk_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "number": "5511999999999",
    "message": "Olá da API!"
  }'
```

**Resposta:**
```json
{
  "success": true,
  "messageId": "3EB0...",
  "timestamp": 1705312200
}
```

---

### POST /send-media

Envia mídia (imagem, vídeo, áudio, documento).

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| number | string | Sim | Número do destinatário |
| media_url | string | Sim | URL pública do arquivo de mídia |
| media_type | string | Sim | Tipo: image, video, audio, document |
| caption | string | Não | Legenda para a mídia |
| filename | string | Não | Nome do arquivo (para documentos) |

---

### POST /send-reaction

Reage a uma mensagem com um emoji.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| message_id | string | Sim | ID da mensagem no WhatsApp |
| remote_jid | string | Sim | JID do chat |
| emoji | string | Sim | Emoji de reação |

---

### POST /delete-message

Deleta uma mensagem enviada.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| message_id | string | Sim | ID da mensagem no WhatsApp |
| remote_jid | string | Sim | JID do chat |

---

### GET /groups

Lista todos os grupos do WhatsApp.

**Resposta:**
```json
{
  "success": true,
  "groups": [
    {
      "jid": "120363001234567890@g.us",
      "subject": "Meu Grupo",
      "participants_count": 15,
      "creation": 1705312200
    }
  ]
}
```

---

### POST /group/create

Cria um novo grupo do WhatsApp.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| subject | string | Sim | Nome do grupo |
| participants | array | Sim | Array de números de telefone |

---

### GET /group/{jid}/metadata

Obtém detalhes e participantes do grupo.

---

### POST /reconnect

Força reconexão ao WhatsApp.

---

### POST /logout

Faz logout e limpa a sessão (requer novo QR code).

---

## Mensagens

### GET /messages

Lista mensagens com filtros.

**Parâmetros Query:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| instance_id | string | UUID da instância |
| conversation_id | integer | ID da conversa |
| direction | string | Direção (incoming, outgoing) |
| status | string | Status (pending, sent, delivered, read, failed) |
| from | datetime | Data inicial |
| to | datetime | Data final |
| search | string | Buscar no corpo da mensagem |
| per_page | integer | Itens por página (padrão: 20) |

---

### POST /messages/send

Envia uma mensagem de texto.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| instance_id | string | Sim | UUID da instância |
| recipient | string | Sim | Número do destinatário (formato: +5511999999999) |
| message | string | Sim | Conteúdo da mensagem (máx. 4096 caracteres) |
| schedule_at | datetime | Não | Agendar envio para data/hora específica |
| reply_to | string | Não | UUID ou ID da mensagem para responder |

**Exemplo de Requisição:**

```bash
curl -X POST https://zapini.app/api/v1/messages/send \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "instance_id": "550e8400-e29b-41d4-a716-446655440000",
    "recipient": "+5511999999999",
    "message": "Olá! Esta é uma mensagem de teste."
  }'
```

**Resposta:**

```json
{
  "success": true,
  "message": "Mensagem adicionada à fila",
  "data": {
    "uuid": "660e8400-e29b-41d4-a716-446655440001",
    "status": "pending",
    "scheduled_at": null,
    "delay_seconds": 15,
    "remaining_quota": 449
  }
}
```

---

### POST /messages/send-media

Envia uma mensagem com mídia.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| instance_id | string | Sim | UUID da instância |
| recipient | string | Sim | Número do destinatário |
| media_url | string | Sim | URL pública do arquivo |
| media_type | string | Sim | Tipo (image, video, audio, document) |
| caption | string | Não | Legenda (máx. 4096 caracteres) |
| filename | string | Não | Nome do arquivo (para documentos) |
| schedule_at | datetime | Não | Agendar envio |

**Exemplo:**

```bash
curl -X POST https://zapini.app/api/v1/messages/send-media \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "instance_id": "550e8400-e29b-41d4-a716-446655440000",
    "recipient": "+5511999999999",
    "media_url": "https://exemplo.com/imagem.jpg",
    "media_type": "image",
    "caption": "Confira esta imagem!"
  }'
```

---

### GET /messages/{uuid}

Retorna detalhes de uma mensagem.

**Resposta:**

```json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440045",
    "unique_id": "660e8400-e29b-41d4-a716-446655440001",
    "direction": "outgoing",
    "sender_number": null,
    "sender_name": null,
    "recipient_number": "5511999999999",
    "message_body": "Olá! Esta é uma mensagem de teste.",
    "media_url": null,
    "media_type": null,
    "status": "delivered",
    "is_edited": false,
    "is_group": false,
    "reactions": [],
    "sent_at": "2025-01-15T10:30:00Z",
    "delivered_at": "2025-01-15T10:30:05Z",
    "read_at": "2025-01-15T10:31:00Z",
    "created_at": "2025-01-15T10:29:45Z",
    "whatsapp_message_id": "BAE5...",
    "conversation_id": 45,
    "error_message": null,
    "retry_count": 0
  }
}
```

---

### GET /messages/{uuid}/status

Retorna apenas o status de uma mensagem.

**Resposta:**

```json
{
  "success": true,
  "data": {
    "uuid": "660e8400-e29b-41d4-a716-446655440001",
    "status": "delivered",
    "sent_at": "2025-01-15T10:30:00Z",
    "delivered_at": "2025-01-15T10:30:05Z",
    "read_at": null,
    "error_message": null,
    "retry_count": 0
  }
}
```

---

### PATCH /messages/{uuid}/edit

Edita uma mensagem já enviada.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| message | string | Sim | Novo conteúdo da mensagem |

**Observações:**
- Apenas mensagens de saída podem ser editadas
- A mensagem deve estar com status sent, delivered ou read

---

### DELETE /messages/{uuid}

Exclui uma mensagem.

**Observações:**
- Apenas mensagens de saída podem ser excluídas
- A mensagem será removida também no WhatsApp do destinatário

---

### POST /messages/{uuid}/reaction

Adiciona uma reação a uma mensagem.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| emoji | string | Sim | Emoji da reação |

---

### DELETE /messages/{uuid}/reaction

Remove a reação de uma mensagem.

---

### GET /messages/{uuid}/reactions

Lista todas as reações de uma mensagem.

---

## Conversas

### GET /conversations

Lista conversas.

**Parâmetros Query:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| instance_id | string | UUID da instância |
| unread | boolean | Apenas conversas com mensagens não lidas |
| search | string | Buscar por nome ou número |
| is_group | boolean | Filtrar grupos ou individuais |
| per_page | integer | Itens por página |

**Resposta:**

```json
{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440045",
      "contact_number": "5511999999999",
      "contact_name": "João Silva",
      "display_name": "João Silva",
      "is_group": false,
      "group_subject": null,
      "profile_picture_url": "https://...",
      "last_message": "Ok, combinado!",
      "last_message_at": "2025-01-15T10:30:00Z",
      "unread_count": 2,
      "is_archived": false
    }
  ],
  "meta": {
    "pagination": {...}
  }
}
```

---

### GET /conversations/archived

Lista conversas arquivadas.

---

### GET /conversations/{uuid}

Retorna detalhes de uma conversa.

---

### GET /conversations/{uuid}/messages

Lista mensagens de uma conversa.

**Parâmetros Query:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| from | datetime | Data inicial |
| to | datetime | Data final |
| before_id | integer | Mensagens antes deste ID (para scroll infinito) |
| after_id | integer | Mensagens após este ID |
| limit | integer | Limite de mensagens (padrão: 50) |

---

### POST /conversations/{uuid}/mark-read

Marca a conversa como lida (zera contador de não lidas).

---

### POST /conversations/{uuid}/archive

Arquiva a conversa.

---

### POST /conversations/{uuid}/unarchive

Desarquiva a conversa.

---

### DELETE /conversations/{uuid}

Exclui a conversa e todas as mensagens.

---

## Controle de Automação

Gerencie a automação de IA para conversas. Quando a automação está ativa, mensagens manuais são bloqueadas até você assumir o chat.

**Base URL:** `/api/v1/chat`

### Importante: Comportamento da Automação

- Quando a automação está **ativa**, mensagens manuais são bloqueadas para evitar conflitos com respostas da IA.
- Use **pause** para assumir o chat e enviar mensagens manualmente.
- Use **resume** para deixar a IA gerenciar a conversa novamente.
- **IMPORTANTE**: Para retomar a automação, você deve primeiro **arquivar** a conversa.

> **Segurança: Arquivamento Obrigatório para Retomar**
>
> A automação só pode ser retomada após arquivar a conversa. Isso garante que a sessão de atendimento manual seja encerrada corretamente antes de devolver o controle à IA. Ao retomar, a conversa será automaticamente desarquivada.

### POST /chat/conversations/{uuid}/pause-automation

Pausa a automação para uma conversa, permitindo mensagens manuais.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| reason | string | Não | Motivo opcional para pausar a automação |

**Exemplo de Requisição:**

```bash
curl -X POST https://zapini.app/api/v1/chat/conversations/550e8400-e29b-41d4-a716-446655440045/pause-automation \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Assumindo manualmente para cliente VIP"}'
```

**Resposta:**

```json
{
  "success": true,
  "message": "Automação pausada com sucesso.",
  "data": {
    "conversation_id": "550e8400-e29b-41d4-a716-446655440045",
    "automation_paused": true,
    "paused_at": "2025-12-20T14:30:00Z",
    "paused_by": "João Silva",
    "reason": "Assumindo manualmente para cliente VIP"
  }
}
```

**Respostas de Erro:**

| Código | Erro | Descrição |
|--------|------|-----------|
| 400 | NO_ACTIVE_AUTOMATION | Conversa não tem automação ativa |
| 400 | AUTOMATION_ALREADY_PAUSED | Automação já está pausada |
| 404 | NOT_FOUND | Conversa não encontrada |

---

### POST /chat/conversations/{uuid}/resume-automation

Retoma a automação para uma conversa, deixando a IA gerenciar as respostas.

> **Nota:** A conversa deve estar arquivada antes de retomar a automação.

**Exemplo de Requisição:**

```bash
curl -X POST https://zapini.app/api/v1/chat/conversations/550e8400-e29b-41d4-a716-446655440045/resume-automation \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json"
```

**Resposta:**

```json
{
  "success": true,
  "message": "Automação retomada com sucesso.",
  "data": {
    "conversation_id": "550e8400-e29b-41d4-a716-446655440045",
    "automation_paused": false,
    "resumed_at": "2025-12-20T15:00:00Z"
  }
}
```

**Respostas de Erro:**

| Código | Erro | Descrição |
|--------|------|-----------|
| 400 | NO_ACTIVE_AUTOMATION | Conversa não tem automação ativa |
| 400 | AUTOMATION_NOT_PAUSED | Automação não está pausada |
| 404 | NOT_FOUND | Conversa não encontrada |

---

## Tags

Gerencie tags de conversas para organizar e categorizar chats. Tags são escopadas por tenant e podem ser atribuídas a múltiplas conversas.

### GET /tags

Lista todas as tags do tenant atual.

**Resposta:**

```json
{
  "success": true,
  "data": {
    "tags": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440001",
        "name": "VIP",
        "color": "#f59e0b",
        "conversations_count": 15,
        "created_at": "2025-01-15T10:30:00Z",
        "updated_at": "2025-01-15T10:30:00Z"
      }
    ]
  }
}
```

---

### POST /tags

Cria uma nova tag.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| name | string | Sim | Nome da tag (máx. 50 caracteres, único por tenant) |
| color | string | Sim | Código de cor hexadecimal (#RRGGBB) |

**Exemplo:**

```bash
curl -X POST https://zapini.app/api/v1/tags \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Importante",
    "color": "#ef4444"
  }'
```

**Resposta:**

```json
{
  "success": true,
  "message": "Tag criada com sucesso",
  "data": {
    "tag": {
      "id": "550e8400-e29b-41d4-a716-446655440003",
      "name": "Importante",
      "color": "#ef4444",
      "conversations_count": 0,
      "created_at": "2025-01-15T12:00:00Z",
      "updated_at": "2025-01-15T12:00:00Z"
    }
  }
}
```

---

### GET /tags/{uuid}

Retorna detalhes de uma tag.

---

### PATCH /tags/{uuid}

Atualiza uma tag existente.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| name | string | Não | Novo nome da tag (máx. 50 caracteres) |
| color | string | Não | Novo código de cor hexadecimal (#RRGGBB) |

---

### DELETE /tags/{uuid}

Exclui uma tag. A tag será automaticamente removida de todas as conversas.

---

### PUT /conversations/{uuid}/tags

Atualiza as tags atribuídas a uma conversa. Isso substitui todas as tags existentes pela lista fornecida.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| tag_ids | array | Sim | Array de IDs de tags para atribuir |

**Exemplo:**

```bash
curl -X PUT https://zapini.app/api/v1/conversations/550e8400-e29b-41d4-a716-446655440045/tags \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "tag_ids": ["550e8400-e29b-41d4-a716-446655440001", "550e8400-e29b-41d4-a716-446655440002", "550e8400-e29b-41d4-a716-446655440003"]
  }'
```

**Resposta:**

```json
{
  "success": true,
  "message": "Tags da conversa atualizadas",
  "data": {
    "tags": [
      {"id": "550e8400-e29b-41d4-a716-446655440001", "name": "VIP", "color": "#f59e0b"},
      {"id": 2, "name": "Suporte", "color": "#3b82f6"},
      {"id": "550e8400-e29b-41d4-a716-446655440003", "name": "Importante", "color": "#ef4444"}
    ]
  }
}
```

**Nota:** Para remover todas as tags de uma conversa, envie um array vazio: `{"tag_ids": []}`

---

## Chat API (Otimizada para UI)

A Chat API fornece endpoints otimizados para criar interfaces de chat tipo WhatsApp. Retorna dados pré-formatados com @menções resolvidas, reações agrupadas e helpers de alinhamento.

**Base URL:** `/api/v1/chat`

### Recursos Principais

| Campo | Descrição |
|-------|-----------|
| `formatted_body` | Texto com @menções resolvidas para nomes (HTML) |
| `is_from_me` | Boolean para alinhamento de mensagens |
| `grouped_reactions` | Reações agrupadas por emoji com contagem |
| `mentioned_jids` | Array de JIDs do WhatsApp mencionados |
| `tags` | Tags da conversa incluídas |

### GET /chat/conversations

Lista conversas otimizadas para UI de chat.

**Parâmetros Query:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| instance_id | string | Filtrar por UUID da instância |
| search | string | Buscar por nome ou número |
| unread | boolean | Apenas conversas não lidas |
| per_page | integer | Itens por página |

**Resposta:**

```json
{
  "success": true,
  "data": {
    "data": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440045",
        "contact_number": "5521999999999",
        "display_name": "João Silva",
        "profile_picture_url": "https://...",
        "last_message": "Olá!",
        "unread_count": 3,
        "tags": [{"id": "550e8400-e29b-41d4-a716-446655440001", "name": "VIP", "color": "#f59e0b"}],
        "instance": {"uuid": "abc-123", "status": "connected"}
      }
    ]
  }
}
```

---

### GET /chat/conversations/{uuid}/messages

Obtém mensagens com corpo formatado e reações agrupadas.

**Parâmetros Query:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| before_id | integer | Mensagens antes deste ID |
| after_id | integer | Mensagens após este ID |
| limit | integer | Limite de mensagens (padrão: 50) |

**Resposta:**

```json
{
  "success": true,
  "data": {
    "messages": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440045"6,
        "direction": "incoming",
        "is_from_me": false,
        "message_body": "Olá @5521999999999!",
        "formatted_body": "Olá <span class=\"mention\">@João</span>!",
        "mentioned_jids": ["5521999999999@s.whatsapp.net"],
        "grouped_reactions": [
          {"emoji": "👍", "count": 2, "has_my_reaction": true}
        ]
      }
    ],
    "has_more": true
  }
}
```

---

### POST /chat/send

Envia mensagem de texto com resposta otimizada para chat.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| instance_id | string | Sim | UUID da instância |
| recipient | string | Sim | Número do destinatário |
| message | string | Sim | Conteúdo da mensagem |
| reply_to | string | Não | UUID da mensagem para responder |

---

### POST /chat/send-media

Envia mídia com resposta otimizada para chat.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| instance_id | string | Sim | UUID da instância |
| recipient | string | Sim | Número do destinatário |
| media_url | string | Sim | URL do arquivo de mídia |
| media_type | string | Sim | Tipo (image, video, audio, document) |
| caption | string | Não | Legenda |

---

### PATCH /chat/messages/{uuid}

Edita uma mensagem enviada.

---

### DELETE /chat/messages/{uuid}

Exclui uma mensagem enviada.

---

### POST /chat/messages/{uuid}/reaction

Adiciona reação emoji.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| emoji | string | Sim | Emoji para reagir |

---

### DELETE /chat/messages/{uuid}/reaction

Remove reação.

---

### POST /chat/conversations/{uuid}/mark-read

Marca conversa como lida.

---

### POST /chat/conversations/{uuid}/archive

Arquiva conversa.

---

### Alternativa: ?format=chat

Use os endpoints padrão com o parâmetro `?format=chat`:

```bash
GET /api/v1/conversations?format=chat
GET /api/v1/conversations/550e8400-e29b-41d4-a716-446655440045/messages?format=chat
```

---

## Contatos

### GET /contacts

Lista contatos.

**Parâmetros Query:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| instance_id | string | UUID da instância |
| search | string | Buscar por nome, número, email ou empresa |
| is_group | boolean | Filtrar grupos |
| sort | string | Campo para ordenação |
| dir | string | Direção (asc, desc) |
| per_page | integer | Itens por página |

**Resposta:**

```json
{
  "success": true,
  "data": [
    {
      "uuid": "770e8400-e29b-41d4-a716-446655440000",
      "phone_number": "5511999999999",
      "display_name": "João Silva",
      "push_name": "João",
      "first_name": "João",
      "last_name": "Silva",
      "full_name": "João Silva",
      "email": "joao@exemplo.com",
      "company": "Empresa ABC",
      "profile_picture_url": "https://...",
      "is_group": false,
      "is_business": true
    }
  ]
}
```

---

### POST /contacts

Cria um novo contato.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| instance_id | string | Sim | UUID da instância |
| phone_number | string | Sim | Número do telefone |
| first_name | string | Não | Primeiro nome |
| last_name | string | Não | Sobrenome |
| display_name | string | Não | Nome de exibição |
| email | string | Não | Email |
| company | string | Não | Empresa |
| job_title | string | Não | Cargo |
| notes | string | Não | Observações |
| custom_fields | object | Não | Campos personalizados |

---

### POST /contacts/import

Importa contatos em massa.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| instance_id | string | Sim | UUID da instância |
| contacts | array | Sim | Lista de contatos (máx. 1000) |

**Exemplo:**

```bash
curl -X POST https://zapini.app/api/v1/contacts/import \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "instance_id": "550e8400-e29b-41d4-a716-446655440000",
    "contacts": [
      {
        "phone_number": "+5511999999999",
        "first_name": "João",
        "last_name": "Silva",
        "email": "joao@exemplo.com"
      },
      {
        "phone_number": "+5511888888888",
        "first_name": "Maria",
        "company": "Empresa XYZ"
      }
    ]
  }'
```

**Resposta:**

```json
{
  "success": true,
  "message": "2 contatos importados",
  "data": {
    "imported": 2,
    "skipped": 0,
    "errors": []
  }
}
```

---

### GET /contacts/{uuid}

Retorna detalhes de um contato.

---

### PATCH /contacts/{uuid}

Atualiza um contato.

---

### DELETE /contacts/{uuid}

Exclui um contato.

---

### GET /contacts/{uuid}/messages

Lista mensagens trocadas com um contato.

---

## Agendamentos

### GET /scheduled

Lista mensagens agendadas.

**Parâmetros Query:**

| Campo | Tipo | Descrição |
|-------|------|-----------|
| instance_id | string | UUID da instância |
| from | datetime | Data inicial |
| to | datetime | Data final |
| per_page | integer | Itens por página |

**Resposta:**

```json
{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "recipient_number": "5511999999999",
      "message_body": "Lembrete: Sua consulta é amanhã!",
      "media_url": null,
      "media_type": null,
      "scheduled_at": "2025-01-16T09:00:00Z",
      "status": "pending",
      "created_at": "2025-01-15T10:00:00Z"
    }
  ]
}
```

---

### POST /scheduled

Agenda uma nova mensagem.

**Parâmetros:**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| instance_id | string | Sim | UUID da instância |
| recipient | string | Sim* | Número do destinatário |
| contact_id | string | Sim* | UUID do contato (alternativa ao recipient) |
| message | string | Sim | Conteúdo da mensagem |
| scheduled_at | datetime | Sim | Data/hora do envio |
| media_url | string | Não | URL da mídia |
| media_type | string | Não | Tipo da mídia |

*Um dos dois é obrigatório: recipient ou contact_id

---

### GET /scheduled/{uuid}

Retorna detalhes de um agendamento.

---

### PATCH /scheduled/{uuid}

Atualiza um agendamento pendente.

---

### DELETE /scheduled/{uuid}

Cancela um agendamento.

---

### POST /scheduled/{uuid}/send-now

Envia a mensagem agendada imediatamente.

---

## Mídia

### POST /media/upload

Faz upload de um arquivo.

**Parâmetros (multipart/form-data):**

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| file | file | Sim | Arquivo a ser enviado |
| type | string | Sim | Tipo (image, video, audio, document) |

**Limites:**

| Tipo | Tamanho Máximo | Formatos Aceitos |
|------|----------------|------------------|
| image | 16 MB | JPEG, PNG, GIF, WebP |
| video | 64 MB | MP4, 3GPP, QuickTime |
| audio | 16 MB | MP3, OGG, Opus, WAV, AAC |
| document | 100 MB | PDF, DOC(X), XLS(X), PPT(X), TXT, CSV |

**Exemplo:**

```bash
curl -X POST https://zapini.app/api/v1/media/upload \
  -H "Authorization: Bearer {token}" \
  -F "file=@/caminho/para/imagem.jpg" \
  -F "type=image"
```

**Resposta:**

```json
{
  "success": true,
  "message": "Arquivo enviado com sucesso",
  "data": {
    "id": "880e8400-e29b-41d4-a716-446655440000",
    "url": "https://storage.zapini.app/media/tenant/images/arquivo.jpg",
    "filename": "imagem.jpg",
    "mime_type": "image/jpeg",
    "size": 1024000,
    "type": "image"
  }
}
```

---

### GET /media/{id}

Retorna detalhes de um arquivo.

---

### DELETE /media/{id}

Exclui um arquivo.

---

## Exemplos de Código

### PHP (Guzzle)

```php
<?php
use GuzzleHttp\Client;

$client = new Client([
    'base_uri' => 'https://zapini.app/api/v1/',
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept' => 'application/json',
    ]
]);

// Login
$response = $client->post('auth/login', [
    'json' => [
        'email' => 'usuario@exemplo.com',
        'password' => 'sua_senha'
    ]
]);
$data = json_decode($response->getBody(), true);
$token = $data['data']['token'];

// Enviar mensagem
$response = $client->post('messages/send', [
    'json' => [
        'instance_id' => '550e8400-e29b-41d4-a716-446655440000',
        'recipient' => '+5511999999999',
        'message' => 'Olá do PHP!'
    ]
]);

$result = json_decode($response->getBody(), true);
echo "Mensagem enviada! UUID: " . $result['data']['uuid'];
```

### Python (Requests)

```python
import requests

BASE_URL = 'https://zapini.app/api/v1'

# Login
response = requests.post(f'{BASE_URL}/auth/login', json={
    'email': 'usuario@exemplo.com',
    'password': 'sua_senha'
})
data = response.json()
token = data['data']['token']

headers = {
    'Authorization': f'Bearer {token}',
    'Accept': 'application/json'
}

# Enviar mensagem
response = requests.post(f'{BASE_URL}/messages/send',
    headers=headers,
    json={
        'instance_id': '550e8400-e29b-41d4-a716-446655440000',
        'recipient': '+5511999999999',
        'message': 'Olá do Python!'
    }
)

result = response.json()
print(f"Mensagem enviada! UUID: {result['data']['uuid']}")
```

### JavaScript (Fetch)

```javascript
const BASE_URL = 'https://zapini.app/api/v1';

// Login
async function login(email, password) {
    const response = await fetch(`${BASE_URL}/auth/login`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        },
        body: JSON.stringify({ email, password })
    });
    const data = await response.json();
    return data.data.token;
}

// Enviar mensagem
async function sendMessage(token, instanceId, recipient, message) {
    const response = await fetch(`${BASE_URL}/messages/send`, {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        },
        body: JSON.stringify({
            instance_id: instanceId,
            recipient: recipient,
            message: message
        })
    });
    return await response.json();
}

// Uso
const token = await login('usuario@exemplo.com', 'sua_senha');
const result = await sendMessage(
    token,
    '550e8400-e29b-41d4-a716-446655440000',
    '+5511999999999',
    'Olá do JavaScript!'
);
console.log('Mensagem enviada! UUID:', result.data.uuid);
```

### Node.js (Axios)

```javascript
const axios = require('axios');

const api = axios.create({
    baseURL: 'https://zapini.app/api/v1',
    headers: {
        'Accept': 'application/json'
    }
});

// Login
async function login(email, password) {
    const { data } = await api.post('/auth/login', { email, password });
    api.defaults.headers.common['Authorization'] = `Bearer ${data.data.token}`;
    return data.data.token;
}

// Enviar mensagem
async function sendMessage(instanceId, recipient, message) {
    const { data } = await api.post('/messages/send', {
        instance_id: instanceId,
        recipient: recipient,
        message: message
    });
    return data;
}

// Uso
(async () => {
    await login('usuario@exemplo.com', 'sua_senha');
    const result = await sendMessage(
        '550e8400-e29b-41d4-a716-446655440000',
        '+5511999999999',
        'Olá do Node.js!'
    );
    console.log('Mensagem enviada! UUID:', result.data.uuid);
})();
```

---

## Webhooks

Configure webhooks para receber eventos em tempo real.

### Eventos Disponíveis

| Evento | Descrição |
|--------|-----------|
| message.received | Nova mensagem recebida |
| message.sent | Mensagem enviada |
| message.delivered | Mensagem entregue |
| message.read | Mensagem lida |
| message.failed | Falha no envio |
| instance.connected | Instância conectada |
| instance.disconnected | Instância desconectada |
| instance.qr_ready | QR Code disponível |

### Formato do Payload

```json
{
  "event": "message.received",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": {
    "message": {
      "unique_id": "...",
      "direction": "incoming",
      "sender_number": "5511999999999",
      "message_body": "Olá!"
    },
    "instance": {
      "uuid": "...",
      "phone_number": "5511888888888"
    }
  }
}
```

---

## Suporte

- **Email:** suporte@zapini.app
- **Documentação:** https://zapini.app/docs

---

*Última atualização: Janeiro 2025*

---

## API Kanban & Calendário

A API Zapini inclui gerenciamento completo de pipeline Kanban e endpoints de Calendário/Agendamentos.

**Veja documentação dedicada:** [kanban-calendar.md](kanban-calendar.md)

### API Kanban (21 endpoints)
- `GET /api/v1/kanban/boards` — Listar quadros
- `POST /api/v1/kanban/boards` — Criar quadro
- `GET /api/v1/kanban/boards/{uuid}` — Obter quadro + estatísticas
- `PUT /api/v1/kanban/boards/{uuid}` — Atualizar quadro
- `DELETE /api/v1/kanban/boards/{uuid}` — Excluir quadro
- `GET /api/v1/kanban/boards/{uuid}/columns` — Listar colunas
- `POST /api/v1/kanban/boards/{uuid}/columns` — Criar coluna
- `POST /api/v1/kanban/boards/{uuid}/columns/reorder` — Reordenar colunas
- `PUT /api/v1/kanban/columns/{uuid}` — Atualizar coluna
- `DELETE /api/v1/kanban/columns/{uuid}` — Excluir coluna
- `GET /api/v1/kanban/boards/{uuid}/leads` — Listar leads (com filtros)
- `POST /api/v1/kanban/leads` — Criar lead
- `GET /api/v1/kanban/leads/{uuid}` — Obter lead
- `PUT /api/v1/kanban/leads/{uuid}` — Atualizar lead
- `DELETE /api/v1/kanban/leads/{uuid}` — Excluir lead
- `POST /api/v1/kanban/leads/{uuid}/move` — Mover para coluna
- `POST /api/v1/kanban/leads/{uuid}/won` — Marcar como ganho
- `POST /api/v1/kanban/leads/{uuid}/lost` — Marcar como perdido
- `POST /api/v1/kanban/leads/{uuid}/note` — Adicionar nota
- `GET /api/v1/kanban/leads/{uuid}/activities` — Histórico de atividades
- `POST /api/v1/kanban/leads/{uuid}/ai-qualify` — Qualificação por IA

### API Calendário (9 endpoints)
- `GET /api/v1/calendar/appointments` — Listar agendamentos
- `POST /api/v1/calendar/appointments` — Criar agendamento
- `GET /api/v1/calendar/appointments/{uuid}` — Obter agendamento
- `PUT /api/v1/calendar/appointments/{uuid}` — Atualizar agendamento
- `DELETE /api/v1/calendar/appointments/{uuid}` — Excluir agendamento
- `POST /api/v1/calendar/appointments/{uuid}/confirm` — Confirmar
- `POST /api/v1/calendar/appointments/{uuid}/complete` — Concluir
- `POST /api/v1/calendar/appointments/{uuid}/cancel` — Cancelar
- `POST /api/v1/calendar/appointments/{uuid}/to-kanban` — Converter em lead Kanban
