{
  "openapi": "3.1.0",
  "info": {
    "title": "TopTalking API",
    "version": "1.1.0",
    "summary": "API da TopTalking para WhatsApp, contatos, Kanban, Agenda e webhooks.",
    "description": "A API da TopTalking deixa o seu sistema (ou a sua IA) conversar com o WhatsApp\nconectado na sua conta: enviar mensagens, buscar e criar contatos, ler o\nhistórico de conversas, mover cards no Kanban, agendar mensagens e receber\neventos por webhook.\n\n**Autenticação:** envie a chave no cabeçalho `Authorization: Bearer tt_live_...`.\nGere a chave no painel em Configurações → Desenvolvedor.\n\n**Nunca use a chave no navegador.** A API não aceita chamadas de front-end\n(CORS bloqueado de propósito): chame a partir do seu servidor, de uma edge\nfunction ou de uma automação.\n\n**Em toda resposta:** cabeçalho `X-Request-Id` (cite-o ao pedir suporte) e os\ncabeçalhos `X-RateLimit-Limit`, `X-RateLimit-Remaining` e `X-RateLimit-Reset`.\nQualquer rota pode responder `401` (chave), `429` (limite por minuto, com\n`Retry-After`), `500`, `502`, `503` e `504`; toda rota de escrita pode responder `403` (chave\nsomente leitura).\n\nDocumentação completa: https://platform.toptalking.com.br\nPara IAs: https://platform.toptalking.com.br/llms.txt\n",
    "contact": {
      "name": "TopTalking",
      "url": "https://platform.toptalking.com.br"
    }
  },
  "servers": [
    {
      "url": "https://platform.toptalking.com.br/v1",
      "description": "Produção"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Conta"
    },
    {
      "name": "Canais",
      "description": "Números de WhatsApp e Chat do site conectados à conta."
    },
    {
      "name": "Contatos",
      "description": "Contatos são as conversas da caixa de entrada (um por número e canal)."
    },
    {
      "name": "Mensagens"
    },
    {
      "name": "Kanban",
      "description": "Etapas do Kanban (pipeline) da organização."
    },
    {
      "name": "Mensagens agendadas",
      "description": "Mensagens programadas, as mesmas da Agenda do painel."
    },
    {
      "name": "Webhooks",
      "description": "Endpoints que recebem eventos da sua conta."
    },
    {
      "name": "Grupos",
      "description": "Grupos do WhatsApp (cada grupo também é uma conversa)."
    },
    {
      "name": "Equipe",
      "description": "Membros da organização (atendentes e administradores)."
    },
    {
      "name": "Agentes de IA",
      "description": "Agentes de IA que respondem as conversas."
    },
    {
      "name": "Campanhas",
      "description": "Disparos em massa pelo WhatsApp Oficial (API da Meta) com templates aprovados."
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "tags": [
          "Conta"
        ],
        "operationId": "getMe",
        "summary": "Dados da chave e da organização",
        "description": "Útil para testar se a chave funciona.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/channels": {
      "get": {
        "tags": [
          "Canais"
        ],
        "operationId": "listChannels",
        "summary": "Listar canais",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Channel"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Canais"
        ],
        "operationId": "createChannel",
        "summary": "Conectar um novo WhatsApp (QR Code)",
        "description": "Cria um canal de WhatsApp e já devolve o QR Code para ler no celular\n(WhatsApp → Aparelhos conectados → Conectar um aparelho). Depois, consulte\n`GET /channels/{channel_id}/qr` até `status` = `connected`.\n\nRespeita o limite de conexões do plano da organização (`422 limit_reached`).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChannelCreate"
              },
              "example": {
                "name": "Vendas"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Canal criado, aguardando a leitura do QR Code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelConnection"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "Limite de conexões do plano atingido (`limit_reached`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/channels/{channel_id}": {
      "get": {
        "tags": [
          "Canais"
        ],
        "operationId": "getChannel",
        "summary": "Buscar canal",
        "parameters": [
          {
            "$ref": "#/components/parameters/ChannelId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Channel"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Canais"
        ],
        "operationId": "updateChannel",
        "summary": "Renomear canal",
        "parameters": [
          {
            "$ref": "#/components/parameters/ChannelId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChannelUpdate"
              },
              "example": {
                "name": "Suporte"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Channel"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Canais"
        ],
        "operationId": "deleteChannel",
        "summary": "Remover canal de WhatsApp",
        "description": "Desconecta e remove o WhatsApp da organização. As conversas continuam no\npainel. O Chat do site só pode ser removido no painel.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/ChannelId"
          }
        ],
        "responses": {
          "200": {
            "description": "Removido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/channels/{channel_id}/connect": {
      "post": {
        "tags": [
          "Canais"
        ],
        "operationId": "connectChannel",
        "summary": "Gerar QR Code (ou código de pareamento)",
        "description": "Inicia o pareamento de um WhatsApp que ainda não está conectado e devolve o\nQR Code. Para conectar sem câmera, envie `phone`: a resposta traz um\n`pairing_code` de 8 caracteres para digitar no celular (WhatsApp →\nAparelhos conectados → Conectar com número de telefone).\n\nO QR Code vale por cerca de 20 a 40 segundos e é renovado automaticamente:\nconsulte `GET /channels/{channel_id}/qr` a cada poucos segundos até o\n`status` virar `connected` (ou assine o webhook `channel.status_updated`).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/ChannelId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChannelConnect"
              },
              "examples": {
                "qr": {
                  "summary": "QR Code",
                  "value": {}
                },
                "pareamento": {
                  "summary": "Código de pareamento",
                  "value": {
                    "phone": "5511999998888"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "QR Code (ou código) gerado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelConnection"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/channels/{channel_id}/qr": {
      "get": {
        "tags": [
          "Canais"
        ],
        "operationId": "getChannelQr",
        "summary": "QR Code atual",
        "description": "O QR Code mais recente do pareamento em andamento, para mostrar na sua\ntela. Não gera um novo pareamento (para isso use `POST /channels/{channel_id}/connect`).\nQuando o celular lê o código, `status` vira `connected` e `qr_code` volta `null`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/ChannelId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelConnection"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/channels/{channel_id}/disconnect": {
      "post": {
        "tags": [
          "Canais"
        ],
        "operationId": "disconnectChannel",
        "summary": "Desconectar WhatsApp",
        "description": "Encerra a sessão do WhatsApp neste canal (o mesmo que \"Desconectar\" no\npainel). As conversas continuam no painel; para usar o número de novo,\nchame `POST /channels/{channel_id}/connect`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/ChannelId"
          }
        ],
        "responses": {
          "200": {
            "description": "Desconectado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Channel"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/messages/{message_id}/reaction": {
      "post": {
        "tags": [
          "Mensagens"
        ],
        "operationId": "reactToMessage",
        "summary": "Reagir a uma mensagem",
        "description": "Envia (ou remove, com `emoji` vazio) uma reação no WhatsApp.",
        "parameters": [
          {
            "$ref": "#/components/parameters/MessageId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "emoji"
                ],
                "properties": {
                  "emoji": {
                    "type": "string",
                    "description": "Um emoji (ex.: \"👍\"). String vazia remove a reação."
                  }
                }
              },
              "example": {
                "emoji": "👍"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ChannelUnavailable"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/contacts/{contact_id}/read": {
      "post": {
        "tags": [
          "Contatos"
        ],
        "operationId": "markContactRead",
        "summary": "Marcar conversa como lida",
        "description": "Zera o contador de não lidas da conversa no painel.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/groups": {
      "get": {
        "tags": [
          "Grupos"
        ],
        "operationId": "listGroups",
        "summary": "Listar grupos",
        "description": "Grupos do WhatsApp que aparecem no painel, dos mais recentes para os mais\nantigos. Um grupo também é uma conversa: use o `id` dele como `contact_id`\npara ler o histórico (`GET /contacts/{contact_id}/messages`) ou enviar\nmensagem (`POST /messages`).\n",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "description": "Parte do nome do grupo.",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "channel_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Group"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/groups/{group_id}/participants": {
      "get": {
        "tags": [
          "Grupos"
        ],
        "operationId": "listGroupParticipants",
        "summary": "Participantes do grupo",
        "description": "Busca na hora, no WhatsApp conectado, quem está no grupo. Precisa do canal conectado.",
        "parameters": [
          {
            "name": "group_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/GroupParticipant"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ChannelUnavailable"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/members": {
      "get": {
        "tags": [
          "Equipe"
        ],
        "operationId": "listMembers",
        "summary": "Listar membros da equipe",
        "description": "Use o `id` do membro em `assigned_to` para atribuir uma conversa.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Member"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/members/{member_id}": {
      "get": {
        "tags": [
          "Equipe"
        ],
        "operationId": "getMember",
        "summary": "Buscar membro",
        "parameters": [
          {
            "name": "member_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/ai-agents": {
      "get": {
        "tags": [
          "Agentes de IA"
        ],
        "operationId": "listAiAgents",
        "summary": "Listar agentes de IA",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/AiAgent"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/ai-agents/{agent_id}": {
      "get": {
        "tags": [
          "Agentes de IA"
        ],
        "operationId": "getAiAgent",
        "summary": "Buscar agente de IA",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiAgent"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Agentes de IA"
        ],
        "operationId": "updateAiAgent",
        "summary": "Ligar ou desligar agente de IA",
        "description": "`active: false` faz o agente parar de responder. Desligar o agente\n**padrão** desliga a IA da organização inteira (o mesmo interruptor do painel).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/AgentId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "active"
                ],
                "properties": {
                  "active": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "active": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AiAgent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/templates": {
      "get": {
        "tags": [
          "Campanhas"
        ],
        "operationId": "listTemplates",
        "summary": "Listar templates",
        "description": "Templates de mensagem do WhatsApp Oficial (API da Meta) da organização.\nSó templates com `ready: true` (aprovados pela TopTalking e pela Meta)\npodem ser usados em campanhas. Templates são criados e enviados para\naprovação no painel, em Campanhas → Templates.\n",
        "parameters": [
          {
            "name": "ready",
            "in": "query",
            "description": "`true` só os prontos para uso.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Template"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/campaigns": {
      "get": {
        "tags": [
          "Campanhas"
        ],
        "operationId": "listCampaigns",
        "summary": "Listar campanhas",
        "description": "Das mais recentes para as mais antigas.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/CampaignStatus"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Campaign"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Campanhas"
        ],
        "operationId": "createCampaign",
        "summary": "Criar campanha",
        "description": "Cria um disparo pelo WhatsApp Oficial com um template pronto (`ready: true`).\nA campanha nasce `pending_approval`: a equipe da TopTalking revisa e, ao\naprovar, o envio começa sozinho (`approved` → `running` → `completed`).\nO preço por mensagem é congelado na criação; consulte antes com\n`POST /campaigns/quote`.\n\n`variables` de cada destinatário preenche, na ordem, as variáveis\n`{{1}}`, `{{2}}`… do corpo do template.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignCreate"
              },
              "example": {
                "name": "Black Friday 2026",
                "template": {
                  "name": "promo_black_friday",
                  "language": "pt_BR"
                },
                "recipients": [
                  {
                    "phone": "5511999998888",
                    "name": "Maria",
                    "variables": [
                      "Maria",
                      "30%"
                    ]
                  },
                  {
                    "phone": "5521988887777",
                    "name": "João",
                    "variables": [
                      "João",
                      "30%"
                    ]
                  }
                ],
                "skip_already_sent": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Criada e enviada para aprovação.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ChannelUnavailable"
          }
        }
      }
    },
    "/campaigns/quote": {
      "post": {
        "tags": [
          "Campanhas"
        ],
        "operationId": "quoteCampaign",
        "summary": "Simular preço da campanha",
        "description": "Quanto a campanha vai custar, pela tabela de preço da sua organização. Não cria nada.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "template",
                  "recipients_count"
                ],
                "properties": {
                  "template": {
                    "$ref": "#/components/schemas/TemplateRef"
                  },
                  "recipients_count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100000
                  }
                }
              },
              "example": {
                "template": {
                  "name": "promo_black_friday",
                  "language": "pt_BR"
                },
                "recipients_count": 1500
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignQuote"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/campaigns/{campaign_id}": {
      "get": {
        "tags": [
          "Campanhas"
        ],
        "operationId": "getCampaign",
        "summary": "Buscar campanha",
        "parameters": [
          {
            "$ref": "#/components/parameters/CampaignId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/campaigns/{campaign_id}/recipients": {
      "get": {
        "tags": [
          "Campanhas"
        ],
        "operationId": "listCampaignRecipients",
        "summary": "Destinatários da campanha",
        "description": "Status de envio de cada destinatário, na ordem em que foram enviados à API.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CampaignId"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "sending",
                "sent",
                "delivered",
                "read",
                "failed"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/CampaignRecipient"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/campaigns/{campaign_id}/pause": {
      "post": {
        "tags": [
          "Campanhas"
        ],
        "operationId": "pauseCampaign",
        "summary": "Pausar campanha",
        "description": "Interrompe o envio de uma campanha `approved` ou `running`. Quem já recebeu não é afetado.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CampaignId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/campaigns/{campaign_id}/resume": {
      "post": {
        "tags": [
          "Campanhas"
        ],
        "operationId": "resumeCampaign",
        "summary": "Retomar campanha",
        "description": "Volta a enviar uma campanha `paused` (continua de onde parou).",
        "parameters": [
          {
            "$ref": "#/components/parameters/CampaignId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/contacts": {
      "get": {
        "tags": [
          "Contatos"
        ],
        "operationId": "listContacts",
        "summary": "Listar contatos",
        "description": "Ordenados pela última mensagem (mais recentes primeiro).",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "description": "Busca por nome ou telefone (parcial).",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "phone",
            "in": "query",
            "description": "Telefone exato (aceita formatado; é normalizado para dígitos).",
            "schema": {
              "type": "string",
              "example": "5511999998888"
            }
          },
          {
            "name": "stage_id",
            "in": "query",
            "description": "Só contatos nesta etapa do Kanban.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "channel_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "archived",
            "in": "query",
            "description": "`true` só arquivados, `false` só ativos. Sem o parâmetro, todos.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "is_group",
            "in": "query",
            "description": "`false` só conversas individuais, `true` só grupos. Sem o parâmetro, todos.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "assigned_to",
            "in": "query",
            "description": "ID do membro responsável, ou `none` para conversas sem responsável.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Contact"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Contatos"
        ],
        "operationId": "upsertContact",
        "summary": "Criar ou atualizar contato",
        "description": "Cria o contato pelo telefone. Se já existir um contato com esse telefone\nna organização, atualiza os campos enviados e devolve `200` em vez de `201`.\nComo há um contato por número **e canal**, quando o telefone existe em mais\nde um canal é atualizada a conversa do `channel_id` informado ou, sem ele,\na que teve mensagem mais recente.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactCreate"
              },
              "example": {
                "phone": "+55 11 99999-8888",
                "name": "Maria Souza",
                "email": "maria@exemplo.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contato já existia e foi atualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "201": {
            "description": "Contato criado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/contacts/{contact_id}": {
      "get": {
        "tags": [
          "Contatos"
        ],
        "operationId": "getContact",
        "summary": "Buscar contato",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Contatos",
          "Kanban"
        ],
        "operationId": "updateContact",
        "summary": "Atualizar contato (e mover no Kanban)",
        "description": "Envie só os campos que quer mudar. Para mover o card no Kanban, envie\n`stage_id` (ou `null` para tirar de qualquer etapa). Mover para a etapa\nfinal (`is_final: true`) também finaliza a conversa, igual ao painel.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactUpdate"
              },
              "example": {
                "stage_id": "9b2f5a8e-6a3b-4d0e-9a51-2a7f4f1d0c11"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/contacts/{contact_id}/messages": {
      "get": {
        "tags": [
          "Contatos",
          "Mensagens"
        ],
        "operationId": "listContactMessages",
        "summary": "Histórico da conversa",
        "description": "Mensagens do contato, das mais recentes para as mais antigas.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Message"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/messages": {
      "post": {
        "tags": [
          "Mensagens"
        ],
        "operationId": "sendMessage",
        "summary": "Enviar mensagem",
        "description": "Envia texto e/ou mídia pelo WhatsApp (ou responde no Chat do site).\nInforme o destino com `to` (telefone) **ou** `contact_id`. Se o telefone\nainda não for contato, o contato é criado e a conversa aparece no painel.\n\nO canal usado é, nesta ordem: `channel_id` (se enviado), o canal da\nconversa (se conectado) ou o WhatsApp principal conectado.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageCreate"
              },
              "examples": {
                "texto": {
                  "summary": "Texto",
                  "value": {
                    "to": "5511999998888",
                    "text": "Olá, Maria! Seu pedido #1234 saiu para entrega."
                  }
                },
                "midia": {
                  "summary": "Documento com legenda",
                  "value": {
                    "contact_id": "3f1c2b9a-7d4e-4c55-8a0e-1b2c3d4e5f60",
                    "text": "Segue o boleto.",
                    "media": {
                      "url": "https://exemplo.com/boleto-1234.pdf",
                      "type": "document",
                      "filename": "boleto-1234.pdf"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Mensagem enviada (status `sent`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ChannelUnavailable"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/messages/{message_id}": {
      "get": {
        "tags": [
          "Mensagens"
        ],
        "operationId": "getMessage",
        "summary": "Buscar mensagem",
        "description": "Use para acompanhar o status de entrega (`sent` → `delivered` → `read`).",
        "parameters": [
          {
            "$ref": "#/components/parameters/MessageId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Mensagens"
        ],
        "operationId": "editMessage",
        "summary": "Editar mensagem enviada",
        "description": "Edita o texto de uma mensagem de texto **enviada** (outbound) em até 15 minutos.",
        "parameters": [
          {
            "$ref": "#/components/parameters/MessageId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "maxLength": 4096
                  }
                }
              },
              "example": {
                "text": "Seu pedido #1234 sai amanhã cedo."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Editada (`edited_at` preenchido).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ChannelUnavailable"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      },
      "delete": {
        "tags": [
          "Mensagens"
        ],
        "operationId": "deleteMessage",
        "summary": "Apagar mensagem",
        "description": "`scope=everyone` (padrão) apaga para todos no WhatsApp: só mensagens\nenviadas, em até 48 horas; o texto vira \"Mensagem apagada\".\n`scope=me` só esconde a mensagem do painel e do histórico\n(`GET /contacts/{contact_id}/messages`); ela continua acessível por `GET /messages/{message_id}`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/MessageId"
          },
          {
            "name": "scope",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "everyone",
                "me"
              ],
              "default": "everyone"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Apagada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ChannelUnavailable"
          },
          "502": {
            "$ref": "#/components/responses/ProviderError"
          }
        }
      }
    },
    "/stages": {
      "get": {
        "tags": [
          "Kanban"
        ],
        "operationId": "listStages",
        "summary": "Listar etapas do Kanban",
        "description": "Na ordem das colunas. A etapa com `is_final: true` finaliza a conversa.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Stage"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/scheduled-messages": {
      "get": {
        "tags": [
          "Mensagens agendadas"
        ],
        "operationId": "listScheduledMessages",
        "summary": "Listar mensagens agendadas",
        "description": "Ordenadas pelo horário de envio (mais próximas primeiro).",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/ScheduledMessageStatus"
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ScheduledMessage"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Mensagens agendadas"
        ],
        "operationId": "createScheduledMessage",
        "summary": "Agendar mensagem",
        "description": "A mensagem aparece na Agenda do painel e é enviada no horário (verificação\na cada minuto). O texto aceita as variáveis `{{nome}}` e `{{telefone}}`.\nNão disponível para conversas do Chat do site nem para grupos.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScheduledMessageCreate"
              },
              "example": {
                "to": "5511999998888",
                "send_at": "2026-12-01T12:00:00-03:00",
                "text": "Oi {{nome}}, passando para lembrar da sua consulta amanhã às 9h."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Agendada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledMessage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/scheduled-messages/{scheduled_message_id}": {
      "get": {
        "tags": [
          "Mensagens agendadas"
        ],
        "operationId": "getScheduledMessage",
        "summary": "Buscar mensagem agendada",
        "parameters": [
          {
            "$ref": "#/components/parameters/ScheduledMessageId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledMessage"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Mensagens agendadas"
        ],
        "operationId": "cancelScheduledMessage",
        "summary": "Cancelar mensagem agendada",
        "description": "Só cancela o que ainda está `pending`. Devolve o item com status `canceled`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ScheduledMessageId"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledMessage"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhooks",
        "summary": "Listar webhooks",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookEndpoint"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhook",
        "summary": "Criar webhook",
        "description": "A resposta traz o `secret` (só desta vez) para você validar a assinatura\ndos eventos. Máximo de 10 webhooks por organização.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              },
              "example": {
                "url": "https://meusistema.com.br/webhooks/toptalking",
                "events": [
                  "message.received",
                  "contact.stage_changed"
                ],
                "description": "CRM interno"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Criado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointWithSecret"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "Limite de 10 webhooks por organização (`limit_reached`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks/{webhook_id}": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhook",
        "summary": "Buscar webhook",
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "updateWebhook",
        "summary": "Atualizar webhook",
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhook",
        "summary": "Excluir webhook",
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          }
        ],
        "responses": {
          "200": {
            "description": "Excluído.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/webhooks/{webhook_id}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "testWebhook",
        "summary": "Enviar evento de teste",
        "description": "Envia um evento `ping` para o endpoint (mesmo se estiver desativado).",
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          }
        ],
        "responses": {
          "202": {
            "description": "Na fila de entrega.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDelivery"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/webhooks/{webhook_id}/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookDeliveries",
        "summary": "Histórico de entregas",
        "description": "Últimas tentativas de entrega deste webhook (eventos ficam guardados por 30 dias).",
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookDelivery"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "webhooks": {
    "message.received": {
      "post": {
        "summary": "Mensagem recebida",
        "description": "Um contato mandou mensagem (WhatsApp, grupo ou Chat do site).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "message.received"
                      },
                      "data": {
                        "$ref": "#/components/schemas/MessageEventData"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido. Qualquer 2xx confirma a entrega."
          }
        }
      }
    },
    "message.sent": {
      "post": {
        "summary": "Mensagem enviada",
        "description": "Uma mensagem saiu pela conta — enviada pela equipe, pela IA, por campanha,\npela Agenda ou pela API. Pode chegar com status `queued`; acompanhe em\n`message.status_updated`.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "message.sent"
                      },
                      "data": {
                        "$ref": "#/components/schemas/MessageEventData"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    },
    "message.status_updated": {
      "post": {
        "summary": "Status de entrega mudou",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "message.status_updated"
                      },
                      "data": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/MessageEventData"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "previous_status": {
                                "$ref": "#/components/schemas/MessageStatus"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    },
    "contact.created": {
      "post": {
        "summary": "Contato criado",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "contact.created"
                      },
                      "data": {
                        "type": "object",
                        "properties": {
                          "contact": {
                            "$ref": "#/components/schemas/Contact"
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    },
    "contact.updated": {
      "post": {
        "summary": "Contato alterado",
        "description": "Disparado quando muda nome, e-mail, etapa, responsável, IA ligada/desligada,\narquivamento ou finalização. `changed_fields` diz o que mudou e `previous`\ntraz os valores anteriores desses campos.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "contact.updated"
                      },
                      "data": {
                        "type": "object",
                        "properties": {
                          "contact": {
                            "$ref": "#/components/schemas/Contact"
                          },
                          "changed_fields": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "name",
                                "email",
                                "stage",
                                "assigned_to",
                                "ai_enabled",
                                "archived_at",
                                "resolved_at"
                              ]
                            }
                          },
                          "previous": {
                            "type": "object",
                            "description": "Valores anteriores, só dos campos alterados.",
                            "additionalProperties": true
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    },
    "contact.stage_changed": {
      "post": {
        "summary": "Card mudou de etapa no Kanban",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "contact.stage_changed"
                      },
                      "data": {
                        "type": "object",
                        "properties": {
                          "contact": {
                            "$ref": "#/components/schemas/Contact"
                          },
                          "stage": {
                            "oneOf": [
                              {
                                "$ref": "#/components/schemas/StageRef"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "previous_stage": {
                            "oneOf": [
                              {
                                "$ref": "#/components/schemas/StageRef"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    },
    "scheduled_message.sent": {
      "post": {
        "summary": "Mensagem agendada enviada",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "scheduled_message.sent"
                      },
                      "data": {
                        "type": "object",
                        "properties": {
                          "scheduled_message": {
                            "$ref": "#/components/schemas/ScheduledMessage"
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    },
    "scheduled_message.failed": {
      "post": {
        "summary": "Mensagem agendada falhou",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "scheduled_message.failed"
                      },
                      "data": {
                        "type": "object",
                        "properties": {
                          "scheduled_message": {
                            "$ref": "#/components/schemas/ScheduledMessage"
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    },
    "channel.status_updated": {
      "post": {
        "summary": "Conexão do WhatsApp mudou",
        "description": "O canal conectou (o QR Code foi lido), caiu ou foi desconectado.\nUse para saber o momento exato em que o pareamento terminou.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "channel.status_updated"
                      },
                      "data": {
                        "type": "object",
                        "properties": {
                          "channel": {
                            "$ref": "#/components/schemas/Channel"
                          },
                          "previous_status": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    },
    "campaign.status_updated": {
      "post": {
        "summary": "Status da campanha mudou",
        "description": "Aprovada, pedido de ajustes, começou a enviar, pausada, concluída ou falhou.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "campaign.status_updated"
                      },
                      "data": {
                        "type": "object",
                        "properties": {
                          "campaign": {
                            "$ref": "#/components/schemas/Campaign"
                          },
                          "previous_status": {
                            "$ref": "#/components/schemas/CampaignStatus"
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    },
    "ping": {
      "post": {
        "summary": "Evento de teste",
        "description": "Enviado pelo botão \"Enviar teste\" do painel ou por `POST /webhooks/{id}/test`.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EventEnvelope"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "type": {
                        "const": "ping"
                      },
                      "data": {
                        "type": "object",
                        "properties": {
                          "webhook_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "message": {
                            "type": "string",
                            "example": "Evento de teste enviado pela TopTalking."
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "tt_live_...",
        "description": "Chave de API gerada em Configurações → Desenvolvedor. Também aceita o cabeçalho `X-Api-Key`."
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "description": "Itens por página (1 a 100).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "Valor de `next_cursor` da página anterior.",
        "schema": {
          "type": "string"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "description": "Opcional. Repetir a mesma requisição com a mesma chave (até 24 h) devolve\na resposta original em vez de executar de novo — use para retentativas seguras.\n",
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      },
      "ChannelId": {
        "name": "channel_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ContactId": {
        "name": "contact_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ScheduledMessageId": {
        "name": "scheduled_message_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "WebhookId": {
        "name": "webhook_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "MessageId": {
        "name": "message_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "AgentId": {
        "name": "agent_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "CampaignId": {
        "name": "campaign_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "Identificador da requisição (ex.: req_6f1d2c3b4a5e). Cite ao pedir suporte.",
        "schema": {
          "type": "string"
        }
      },
      "X-RateLimit-Limit": {
        "description": "Requisições permitidas por minuto para esta chave.",
        "schema": {
          "type": "integer",
          "example": 120
        }
      },
      "X-RateLimit-Remaining": {
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Reset": {
        "description": "Unix timestamp (segundos) de quando a janela reinicia.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Requisição inválida.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "invalid_request_error",
                "code": "missing_field",
                "message": "Informe `to` ou `contact_id`.",
                "param": "to",
                "request_id": "req_6f1d2c3b4a5e"
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Chave ausente, inválida, revogada ou expirada.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "authentication_error",
                "code": "invalid_api_key",
                "message": "Chave de API inválida.",
                "request_id": "req_6f1d2c3b4a5e"
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "A chave é somente leitura.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "permission_error",
                "code": "read_only_key",
                "message": "Esta chave é somente leitura.",
                "request_id": "req_6f1d2c3b4a5e"
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Não encontrado (ou de outra organização).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "Conflito de estado ou Idempotency-Key reutilizada com outro corpo.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ChannelUnavailable": {
        "description": "Não há canal conectado para a operação: WhatsApp desconectado ou, em\ncampanhas, nenhum número do WhatsApp Oficial conectado.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "channel_error",
                "code": "channel_unavailable",
                "message": "WhatsApp desconectado. Reconecte no painel.",
                "request_id": "req_6f1d2c3b4a5e"
              }
            }
          }
        }
      },
      "ProviderError": {
        "description": "O WhatsApp recusou a operação ou não respondeu (envio, reação, edição,\npareamento, participantes). Num envio, a mensagem fica registrada com\nstatus `failed` e o erro traz `message_id`.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Limite de requisições por minuto excedido. Veja o cabeçalho `Retry-After`.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "type",
              "code",
              "message",
              "request_id"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "invalid_request_error",
                  "authentication_error",
                  "permission_error",
                  "not_found_error",
                  "conflict_error",
                  "channel_error",
                  "rate_limit_error",
                  "api_error"
                ]
              },
              "code": {
                "type": "string",
                "description": "Código estável para tratar no seu código (status HTTP → type → code):\n- 400 invalid_request_error: invalid_json, missing_field, invalid_field, invalid_phone\n- 401 authentication_error: missing_api_key, invalid_api_key, revoked_api_key, expired_api_key\n- 403 permission_error: read_only_key\n- 404 not_found_error: resource_not_found, route_not_found\n- 405 invalid_request_error: method_not_allowed\n- 409 conflict_error: idempotency_conflict, invalid_state\n- 422 invalid_request_error: limit_reached\n- 422 channel_error: channel_unavailable\n- 429 rate_limit_error: rate_limit_exceeded\n- 500 api_error: internal_error\n- 502 channel_error: provider_error (traz também `message_id` da mensagem gravada como failed)\n- 502 api_error: internal_error (falha passageira)\n- 503 api_error: service_unavailable (API temporariamente indisponível)\n- 504 api_error: timeout (a requisição demorou demais)\n"
              },
              "message": {
                "type": "string",
                "description": "Mensagem em português para humanos."
              },
              "param": {
                "type": "string",
                "description": "Campo que causou o erro",
                "quando houver.": null
              },
              "request_id": {
                "type": "string"
              }
            }
          }
        }
      },
      "ListEnvelope": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {}
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Passe em `cursor` para buscar a próxima página."
          }
        }
      },
      "Deleted": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "example": "channel",
            "description": "Tipo do objeto removido (ex.: channel, webhook_endpoint)."
          },
          "deleted": {
            "const": true
          }
        }
      },
      "Me": {
        "type": "object",
        "properties": {
          "object": {
            "const": "account"
          },
          "organization": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "api_key": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "prefix": {
                "type": "string",
                "example": "tt_live_a1b2c3d4"
              },
              "access": {
                "type": "string",
                "enum": [
                  "full",
                  "read"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "rate_limit": {
            "type": "object",
            "properties": {
              "limit": {
                "type": "integer",
                "example": 120
              },
              "remaining": {
                "type": "integer"
              },
              "reset_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "Channel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "channel"
          },
          "type": {
            "type": "string",
            "enum": [
              "whatsapp",
              "webchat"
            ],
            "description": "`whatsapp` = número conectado; `webchat` = Chat do site."
          },
          "name": {
            "type": "string",
            "example": "Vendas"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "example": "5511999998888"
          },
          "status": {
            "type": "string",
            "enum": [
              "connected",
              "connecting",
              "disconnected"
            ],
            "description": "`connected` = pronto para enviar; `connecting` = aguardando a leitura do QR Code ou reconectando; `disconnected` = desconectado.",
            "example": "connected"
          },
          "is_primary": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ChannelCreate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 80,
            "description": "Nome do canal no painel (ex.: \"Vendas\")."
          }
        }
      },
      "ChannelUpdate": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 80
          }
        }
      },
      "ChannelConnect": {
        "type": "object",
        "properties": {
          "phone": {
            "type": "string",
            "description": "Opcional. Com o número (DDI + DDD), a resposta traz `pairing_code` em vez do QR Code.",
            "example": "5511999998888"
          }
        }
      },
      "ChannelConnection": {
        "type": "object",
        "description": "Estado do pareamento de um canal de WhatsApp.",
        "properties": {
          "object": {
            "const": "channel_connection"
          },
          "channel": {
            "$ref": "#/components/schemas/Channel"
          },
          "status": {
            "type": "string",
            "enum": [
              "connected",
              "connecting",
              "disconnected"
            ],
            "description": "`connected` = o celular leu o código e o canal já envia mensagens."
          },
          "qr_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Imagem PNG em data URL (`data:image/png;base64,...`), pronta para `<img src>`. `null` se conectado ou sem pareamento em andamento."
          },
          "qr_code_generated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "pairing_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Código de 8 caracteres, exibido com hífen no meio (só quando `phone` foi enviado em `/connect`).",
            "example": "WZYE-H1YY"
          }
        }
      },
      "Group": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "É o `contact_id` do grupo: use para ler o histórico e enviar mensagens."
          },
          "object": {
            "const": "group"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "jid": {
            "type": [
              "string",
              "null"
            ],
            "example": "120363012345678901@g.us"
          },
          "channel_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "unread_count": {
            "type": "integer"
          },
          "last_message_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_message_preview": {
            "type": [
              "string",
              "null"
            ]
          },
          "archived_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GroupParticipant": {
        "type": "object",
        "properties": {
          "object": {
            "const": "group_participant"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "example": "5511999998888"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "member",
              "admin",
              "superadmin"
            ],
            "description": "`superadmin` = quem criou o grupo."
          },
          "avatar_url": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Member": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "ID do usuário. Use em `assigned_to`."
          },
          "object": {
            "const": "member"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "agent"
            ]
          },
          "avatar_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "joined_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AiAgent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "ai_agent"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "active": {
            "type": "boolean",
            "description": "Se o agente está respondendo."
          },
          "is_default": {
            "type": "boolean",
            "description": "Agente usado nas conversas sem agente definido."
          },
          "tone": {
            "type": [
              "string",
              "null"
            ],
            "example": "profissional_caloroso"
          },
          "strict_mode": {
            "type": "boolean",
            "description": "Só responde com base nas FAQs, na base de conhecimento e nas instruções."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TemplateRef": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "promo_black_friday"
          },
          "language": {
            "type": "string",
            "default": "pt_BR"
          }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "template"
          },
          "name": {
            "type": "string",
            "example": "promo_black_friday"
          },
          "language": {
            "type": "string",
            "example": "pt_BR"
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "MARKETING",
              "UTILITY",
              "AUTHENTICATION",
              null
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Status na Meta (APPROVED, PENDING, REJECTED, PAUSED, DISABLED)."
          },
          "review_status": {
            "type": "string",
            "description": "Revisão interna da TopTalking: approved, pending ou rejected."
          },
          "ready": {
            "type": "boolean",
            "description": "`true` = pode ser usado em campanhas."
          },
          "body": {
            "type": [
              "string",
              "null"
            ],
            "description": "Texto do corpo com as variáveis `{{1}}`, `{{2}}`…"
          },
          "variables_count": {
            "type": "integer",
            "description": "Tamanho de `variables` em cada destinatário: as variáveis do corpo mais 1 por botão de link com variável (ex.: `https://site.com/checkout/{{1}}`)."
          },
          "header_type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "TEXT",
              "IMAGE",
              "VIDEO",
              "DOCUMENT",
              null
            ]
          },
          "rejected_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CampaignStatus": {
        "type": "string",
        "enum": [
          "draft",
          "pending_approval",
          "approved",
          "changes_requested",
          "running",
          "paused",
          "completed",
          "failed"
        ],
        "description": "`draft` = rascunho criado no painel (campanhas da API já nascem\n`pending_approval`); `pending_approval` = aguardando a revisão da TopTalking; `changes_requested` = a\nrevisão pediu ajustes (veja `review_notes`); `approved` = aprovada, começa a enviar\nem instantes; `running` = enviando; `paused`; `completed`; `failed`.\n"
      },
      "Campaign": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "campaign"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/CampaignStatus"
          },
          "review_notes": {
            "type": [
              "string",
              "null"
            ],
            "description": "Observação da revisão (quando houver)."
          },
          "template": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "language": {
                "type": "string"
              },
              "category": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "totals": {
            "type": "object",
            "description": "Acumulados: `sent` inclui as entregues e lidas; `delivered` inclui as lidas.\n`recipients` = `pending` + `sent` + `failed`.\n",
            "properties": {
              "recipients": {
                "type": "integer"
              },
              "pending": {
                "type": "integer"
              },
              "sent": {
                "type": "integer"
              },
              "delivered": {
                "type": "integer"
              },
              "read": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              }
            }
          },
          "pricing": {
            "type": "object",
            "properties": {
              "unit_price": {
                "type": "number",
                "example": 0.35,
                "description": "Preço por mensagem, congelado na criação."
              },
              "currency": {
                "type": "string",
                "example": "BRL"
              },
              "estimated_total": {
                "type": "number",
                "description": "unit_price × destinatários."
              },
              "charged_total": {
                "type": "number",
                "description": "unit_price × mensagens já enviadas."
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "submitted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "reviewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CampaignCreate": {
        "type": "object",
        "description": "Corpo de até 1 MB. Telefones repetidos na mesma requisição recebem uma\nmensagem só. Valores numéricos em `variables` viram texto.\n",
        "required": [
          "name",
          "template",
          "recipients"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "template": {
            "$ref": "#/components/schemas/TemplateRef"
          },
          "recipients": {
            "type": "array",
            "minItems": 1,
            "maxItems": 5000,
            "items": {
              "type": "object",
              "required": [
                "phone"
              ],
              "properties": {
                "phone": {
                  "type": "string",
                  "example": "5511999998888"
                },
                "name": {
                  "type": "string"
                },
                "variables": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Valores de `{{1}}`, `{{2}}`… do corpo, na ordem. Se o template tiver botão de link com variável, o valor dele vem no fim da lista (só a parte que entra no lugar de `{{1}}`, ex.: `86`)."
                }
              }
            }
          },
          "skip_already_sent": {
            "type": "boolean",
            "default": false,
            "description": "Pula quem já recebeu alguma campanha desta organização. Se todos já receberam: 400 `invalid_field` (param `recipients`)."
          }
        }
      },
      "CampaignQuote": {
        "type": "object",
        "properties": {
          "object": {
            "const": "campaign_quote"
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "example": "MARKETING"
          },
          "recipients": {
            "type": "integer"
          },
          "unit_price": {
            "type": "number",
            "example": 0.35
          },
          "currency": {
            "type": "string",
            "example": "BRL"
          },
          "total": {
            "type": "number",
            "example": 525
          }
        }
      },
      "CampaignRecipient": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "campaign_recipient"
          },
          "phone": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "contact_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "variables": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "sending",
              "sent",
              "delivered",
              "read",
              "failed"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "read_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "StageRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "`null` se a etapa foi apagada depois (o nome antigo continua em `name`)."
          },
          "name": {
            "type": "string",
            "example": "Negociação"
          },
          "is_final": {
            "type": "boolean"
          }
        }
      },
      "Stage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "stage"
          },
          "name": {
            "type": "string",
            "example": "Negociação"
          },
          "color": {
            "type": "string",
            "example": "#8b5cf6"
          },
          "position": {
            "type": "integer",
            "description": "Ordem da coluna (0 = primeira)."
          },
          "is_final": {
            "type": "boolean",
            "description": "Etapa terminal (\"Finalizado\"). Mover um card para ela finaliza a conversa."
          }
        }
      },
      "Contact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "contact"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "example": "Maria Souza"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Só dígitos",
            "com DDI.": null,
            "example": "5511999998888"
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "channel_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "channel_type": {
            "type": "string",
            "enum": [
              "whatsapp",
              "webchat"
            ]
          },
          "is_group": {
            "type": "boolean",
            "description": "Grupo do WhatsApp (sem IA). Envie mensagem usando o id do grupo como `contact_id`."
          },
          "stage": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/StageRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "ai_enabled": {
            "type": "boolean",
            "description": "Se a IA responde esta conversa."
          },
          "ai_agent_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Agente de IA da conversa (`null` = agente padrão)."
          },
          "assigned_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Membro responsável (veja GET /members)."
          },
          "pinned": {
            "type": "boolean",
            "description": "Conversa fixada no topo."
          },
          "unread_count": {
            "type": "integer"
          },
          "last_message_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_message_preview": {
            "type": [
              "string",
              "null"
            ]
          },
          "resolved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Quando a conversa foi finalizada."
          },
          "archived_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "source": {
            "type": "string",
            "example": "api",
            "description": "Origem: whatsapp, webchat, api, agenda, lead..."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ContactCreate": {
        "type": "object",
        "required": [
          "phone"
        ],
        "properties": {
          "phone": {
            "type": "string",
            "description": "Com DDI e DDD. Números brasileiros sem `55` recebem o `55`.",
            "example": "+55 11 99999-8888"
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "stage_id": {
            "type": "string",
            "format": "uuid",
            "description": "Já coloca o card nesta etapa."
          },
          "channel_id": {
            "type": "string",
            "format": "uuid",
            "description": "Canal de WhatsApp da conversa. Padrão = WhatsApp principal."
          }
        }
      },
      "ContactUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "stage_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Move o card no Kanban. `null` tira de qualquer etapa."
          },
          "ai_enabled": {
            "type": "boolean",
            "description": "Liga ou desliga a IA nesta conversa."
          },
          "ai_agent_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Agente de IA desta conversa (veja GET /ai-agents). `null` volta para o agente padrão."
          },
          "assigned_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Membro responsável pela conversa (veja GET /members). `null` libera a conversa."
          },
          "pinned": {
            "type": "boolean",
            "description": "Fixa a conversa no topo da caixa de entrada."
          },
          "resolved": {
            "type": "boolean",
            "description": "`true` finaliza a conversa; `false` reabre."
          },
          "archived": {
            "type": "boolean"
          }
        }
      },
      "MessageStatus": {
        "type": "string",
        "enum": [
          "queued",
          "sent",
          "delivered",
          "read",
          "failed"
        ]
      },
      "Media": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "mime_type": {
            "type": [
              "string",
              "null"
            ],
            "example": "application/pdf"
          },
          "filename": {
            "type": [
              "string",
              "null"
            ],
            "example": "boleto-1234.pdf"
          }
        }
      },
      "Message": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "message"
          },
          "contact_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "channel_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ],
            "description": "`inbound` = o contato mandou."
          },
          "type": {
            "type": "string",
            "description": "Valores comuns: text, image, video, audio, document, sticker. Trate valores desconhecidos como mídia genérica.",
            "example": "text"
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "description": "Texto ou legenda. Mídia sem legenda pode trazer um rótulo como \"📷 Foto\"."
          },
          "media": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Media"
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "$ref": "#/components/schemas/MessageStatus"
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Motivo da falha quando `status` = `failed`."
          },
          "reply_to_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Mensagem citada (resposta)."
          },
          "reaction": {
            "type": [
              "string",
              "null"
            ],
            "description": "Última reação nesta mensagem, nossa ou do contato (ex.: 👍)."
          },
          "sender_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Em grupos",
            "quem mandou.": null
          },
          "transcript": {
            "type": [
              "string",
              "null"
            ],
            "description": "Transcrição de áudio",
            "quando houver.": null
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "edited_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "MediaInput": {
        "type": "object",
        "required": [
          "url",
          "type"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "URL pública HTTPS do arquivo."
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio",
              "document"
            ]
          },
          "filename": {
            "type": "string",
            "description": "Nome exibido (documentos). Padrão = nome na URL."
          },
          "mime_type": {
            "type": "string",
            "description": "Padrão = deduzido pela extensão."
          },
          "voice_note": {
            "type": "boolean",
            "default": false,
            "description": "Com `type: audio`, envia como nota de voz (sem legenda)."
          }
        }
      },
      "MessageCreate": {
        "type": "object",
        "description": "Informe `to` ou `contact_id`, e `text` e/ou `media`.",
        "properties": {
          "to": {
            "type": "string",
            "description": "Telefone com DDI e DDD. Números brasileiros sem `55` recebem o `55`.",
            "example": "5511999998888"
          },
          "contact_id": {
            "type": "string",
            "format": "uuid",
            "description": "Conversa de destino (também aceita o `id` de um grupo, veja GET /groups)."
          },
          "channel_id": {
            "type": "string",
            "format": "uuid"
          },
          "text": {
            "type": "string",
            "maxLength": 4096,
            "description": "Aceita a formatação do WhatsApp (*negrito*",
            "_itálico_).": null
          },
          "media": {
            "$ref": "#/components/schemas/MediaInput"
          },
          "reply_to_id": {
            "type": "string",
            "format": "uuid",
            "description": "Responde (cita) esta mensagem da conversa."
          },
          "contact_name": {
            "type": "string",
            "description": "Nome usado se o contato ainda não existir."
          }
        }
      },
      "ScheduledMessageStatus": {
        "type": "string",
        "enum": [
          "pending",
          "processing",
          "sent",
          "failed",
          "canceled"
        ]
      },
      "ScheduledMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "scheduled_message"
          },
          "contact_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Telefone de destino."
          },
          "title": {
            "type": "string"
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          },
          "media": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Media"
            }
          },
          "send_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/ScheduledMessageStatus"
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "message_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Mensagem gerada no envio."
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ScheduledMessageCreate": {
        "type": "object",
        "required": [
          "send_at"
        ],
        "description": "Informe `to` ou `contact_id`, e `text` e/ou `media`.",
        "properties": {
          "to": {
            "type": "string",
            "example": "5511999998888"
          },
          "contact_id": {
            "type": "string",
            "format": "uuid"
          },
          "send_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 com fuso. No futuro e em até 365 dias."
          },
          "text": {
            "type": "string",
            "maxLength": 4096,
            "description": "Aceita {{nome}} e {{telefone}}."
          },
          "media": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/MediaInput"
            },
            "description": "O texto vira legenda do primeiro anexo que aceita legenda."
          },
          "title": {
            "type": "string",
            "maxLength": 120,
            "description": "Título na Agenda. Padrão = \"Mensagem via API\"."
          }
        }
      },
      "WebhookEventType": {
        "type": "string",
        "enum": [
          "*",
          "message.received",
          "message.sent",
          "message.status_updated",
          "contact.created",
          "contact.updated",
          "contact.stage_changed",
          "scheduled_message.sent",
          "scheduled_message.failed",
          "channel.status_updated",
          "campaign.status_updated"
        ]
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "webhook_endpoint"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "enabled": {
            "type": "boolean"
          },
          "disabled_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Preenchido quando a TopTalking desativou por falhas seguidas."
          },
          "last_delivery_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_delivery_status": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Status HTTP da última entrega."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEndpointWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "example": "whsec_3f9a...",
                "description": "Use para validar `X-TopTalking-Signature`."
              }
            }
          }
        ]
      },
      "WebhookCreate": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Precisa ser HTTPS."
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "default": [
              "*"
            ]
          },
          "description": {
            "type": "string",
            "maxLength": 200
          },
          "enabled": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "WebhookUpdate": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "enabled": {
            "type": "boolean"
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "const": "webhook_delivery"
          },
          "webhook_id": {
            "type": "string",
            "format": "uuid"
          },
          "event_id": {
            "type": "string",
            "format": "uuid"
          },
          "event_type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "succeeded",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "response_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "duration_ms": {
            "type": [
              "integer",
              "null"
            ]
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EventEnvelope": {
        "type": "object",
        "description": "Corpo de todo evento de webhook. Cabeçalhos enviados:\n`X-TopTalking-Signature: t=<unix>,v1=<hex>` (HMAC-SHA256 de \"<t>.<corpo>\" com o secret),\n`X-TopTalking-Event`, `X-TopTalking-Event-Id`, `X-TopTalking-Delivery-Id`.\n",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "ID do evento (o mesmo em retentativas — use para deduplicar)."
          },
          "object": {
            "const": "event"
          },
          "type": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "organization_id": {
            "type": "string",
            "format": "uuid"
          },
          "data": {
            "type": "object"
          }
        }
      },
      "MessageEventData": {
        "type": "object",
        "properties": {
          "message": {
            "$ref": "#/components/schemas/Message"
          },
          "contact": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Contact"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      }
    }
  }
}
