# LeadB2B — Especificação MVP: Conta, Admin e API

Documento de análise do projeto atual cruzado com a visão de MVP de prospecção guiada, incluindo modelo de banco, endpoints, processos e solicitação formal de construção.

---

## 1. Diagnóstico do estado atual

### O que já existe e funciona

| Camada | Situação |
|--------|----------|
| **Servidor Go** (`cmd/server`) | Chi router, templates Go para home/busca/empresa, rate limit, SEO |
| **Global Leads API** | Proxy server-side com `GLOBALAPI_KEY` — busca, filtros, ficha CNPJ |
| **Busca pública** (`/busca`) | Filtros completos: UF, município, CNAE, telefone, e-mail, sócios, CEP, MEI, etc. |
| **Ficha empresa** (`/empresa/{slug}`) | Modo bloqueado (SEO) vs premium — contatos visíveis só no premium |
| **Filtros AJAX** | `/api/filtros/ufs`, `/municipios`, `/cnaes` com cache 15 min |
| **MySQL remoto** (`globals_rfempresas`) | Usado só para stats da home e sitemaps — **não guarda usuários nem leads do produto** |
| **Páginas estáticas** | login, cadastro, pricing, FAQ, blog — servidas via embed, **sem backend** |
| **Protótipos de dashboard** | `dashboard.html`, `dash-favoritos`, `dash-exportacoes`, etc. — **existem em `site/` mas não estão registrados no router** |

### O que ainda não existe

- Autenticação de usuário (login/cadastro são HTML puro)
- Banco de dados de produto (contas, planos, kanban, templates)
- API própria do LeadB2B (`/api/v1/...` além dos filtros)
- Kanban, ação “Trabalhar lead”, limites de leads ativos
- Controle anti-extração por usuário
- Painel admin
- Integração JWT com a Global API (hoje só API Key no servidor)

### Arquitetura atual (simplificada)

```mermaid
flowchart LR
    Browser --> GoApp[LeadB2B Go :8080]
    GoApp -->|API Key| GlobalAPI[leads.globals.tech]
    GoApp --> StatsJSON[data/stats.json]
    Scripts -->|MySQL read-only| RFDB[(globals_rfempresas)]
    GlobalAPI --> RFDB
```

---

## 2. Cruzamento MVP × projeto atual

| Requisito MVP | Hoje | Gap |
|---------------|------|-----|
| Criar conta / login / recuperar senha | HTML estático, sem POST | **100% a construir** |
| Usuário só acessa se plano ativo | Não existe | Middleware + tabela `subscriptions` |
| Plano define leads ativos, exportação | Pricing é visual; sem lógica | Tabela `plans` + enforcement na API |
| Busca com filtros CNAE/cidade/contato | ✅ Global API + `/busca` | Adaptar para usuário logado + mascarar dados |
| “Trabalhar lead” | Não existe | Endpoint + tabela `user_leads` |
| Lead ativo (exceto fechado/perdido/arquivado) | Não existe | Campo `stage` + contador |
| Kanban 7 colunas | Não existe; `dash-favoritos` é bookmark genérico | Nova página + API |
| WhatsApp / e-mail facilitado | Só badges na listagem | Botões pós-“trabalhar” com template |
| Mensagem padrão com variáveis | Não existe | Tabela `message_templates` |
| Anti-extração | Busca pública mostra badges de contato | Regras por sessão/plano |
| Indicadores | Dashboard com conteúdo placeholder | `GET /me/stats` |
| Teste grátis | Texto no pricing | Plano `trial` automático no register |

### Decisão arquitetural central

Existem **duas camadas distintas**:

1. **Global Leads API** — dados RFB, auth JWT, `/auth/me` com limites de conta CRM
2. **LeadB2B (produto)** — funil, kanban, planos comerciais, anti-extração, admin

A API do MVP **não substitui** a Global API. Ela **orquestra** ela.

```mermaid
flowchart TB
    subgraph frontend [Front LeadB2B]
        Public[Site público SEO]
        App[App logado - busca/kanban]
        Admin[Painel admin]
    end

    subgraph leadb2b_api [API LeadB2B - NOVA]
        Auth[Auth proxy + sessão]
        Product[Leads/Kanban/Planos]
        Export[Export controlado]
    end

    subgraph data [Dados]
        AppDB[(leadb2b_app MySQL)]
        GlobalAPI[Global Leads API]
        RFDB[(globals_rfempresas)]
    end

    Public --> leadb2b_api
    App --> leadb2b_api
    Admin --> leadb2b_api
    Auth --> GlobalAPI
    Product --> GlobalAPI
    Product --> AppDB
    Export --> AppDB
    GlobalAPI --> RFDB
```

---

## 3. Modelo de banco de dados proposto (`leadb2b_app`)

Separado de `globals_rfempresas`. Referência de empresa = **CNPJ 14 dígitos** (não duplicar cadastro RFB).

```sql
-- PLANOS
CREATE TABLE plans (
  id              INT PRIMARY KEY AUTO_INCREMENT,
  slug            VARCHAR(50) UNIQUE NOT NULL,  -- trial, starter, pro
  name            VARCHAR(100) NOT NULL,
  max_active_leads INT NOT NULL DEFAULT 10,
  max_add_per_day  INT NULL,                     -- NULL = sem limite diário no MVP
  max_export_per_day INT NOT NULL DEFAULT 0,
  can_export      BOOLEAN NOT NULL DEFAULT FALSE,
  price_cents     INT NOT NULL DEFAULT 0,
  is_active       BOOLEAN NOT NULL DEFAULT TRUE
);

-- USUÁRIOS (espelho local; auth principal na Global API)
CREATE TABLE users (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  global_user_id  VARCHAR(36) NULL,              -- ID retornado pela Global API
  email           VARCHAR(255) UNIQUE NOT NULL,
  password_hash   VARCHAR(255) NULL,            -- se auth própria; ou NULL se só JWT proxy
  name            VARCHAR(200),
  role            ENUM('user','admin') DEFAULT 'user',
  status          ENUM('pending','active','suspended') DEFAULT 'pending',
  created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at      TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- ASSINATURA / PLANO ATIVO
CREATE TABLE subscriptions (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id         BIGINT NOT NULL,
  plan_id         INT NOT NULL,
  status          ENUM('trial','active','expired','cancelled') NOT NULL,
  starts_at       DATETIME NOT NULL,
  ends_at         DATETIME NULL,
  FOREIGN KEY (user_id) REFERENCES users(id),
  FOREIGN KEY (plan_id) REFERENCES plans(id)
);

-- LEAD DO USUÁRIO (funil)
CREATE TABLE user_leads (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id         BIGINT NOT NULL,
  cnpj            CHAR(14) NOT NULL,
  stage           ENUM('novos','contatando','respondeu','negociacao',
                       'fechado','perdido','arquivado') NOT NULL DEFAULT 'novos',
  -- snapshot mínimo para listagem rápida (evita bater na API toda hora)
  company_name    VARCHAR(300),
  city            VARCHAR(100),
  uf              CHAR(2),
  has_phone       BOOLEAN DEFAULT FALSE,
  has_email       BOOLEAN DEFAULT FALSE,
  worked_at       DATETIME NOT NULL,            -- quando clicou "Trabalhar"
  stage_changed_at DATETIME NOT NULL,
  notes           TEXT NULL,
  UNIQUE KEY uq_user_cnpj (user_id, cnpj),
  INDEX idx_user_stage (user_id, stage),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

-- HISTÓRICO DE MOVIMENTAÇÃO (auditoria + retenção)
CREATE TABLE lead_stage_history (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_lead_id    BIGINT NOT NULL,
  from_stage      VARCHAR(20),
  to_stage        VARCHAR(20) NOT NULL,
  changed_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_lead_id) REFERENCES user_leads(id)
);

-- CONTATOS REGISTRADOS (anti-abuso)
CREATE TABLE contact_events (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_lead_id    BIGINT NOT NULL,
  channel         ENUM('whatsapp','email','copy_email') NOT NULL,
  created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_lead_id) REFERENCES user_leads(id)
);

-- MENSAGENS PADRÃO
CREATE TABLE message_templates (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id         BIGINT NOT NULL,
  name            VARCHAR(100) NOT NULL,
  body            TEXT NOT NULL,                -- suporta {{empresa}}
  is_default      BOOLEAN DEFAULT FALSE,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

-- EXPORTAÇÕES (controle diário)
CREATE TABLE export_jobs (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id         BIGINT NOT NULL,
  lead_ids        JSON NOT NULL,                -- só leads já trabalhados
  row_count       INT NOT NULL,
  created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

-- CONTADOR DIÁRIO (leads adicionados / exportações)
CREATE TABLE daily_usage (
  user_id         BIGINT NOT NULL,
  usage_date      DATE NOT NULL,
  leads_added     INT DEFAULT 0,
  exports_count   INT DEFAULT 0,
  PRIMARY KEY (user_id, usage_date)
);

-- SESSÕES / TOKENS (se não usar só cookie HttpOnly com JWT Global)
CREATE TABLE sessions (
  id              VARCHAR(64) PRIMARY KEY,
  user_id         BIGINT NOT NULL,
  global_token    TEXT NULL,
  expires_at      DATETIME NOT NULL,
  ip_address      VARCHAR(45),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

-- ADMIN: logs
CREATE TABLE audit_log (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  actor_user_id   BIGINT,
  action          VARCHAR(100),
  target_type     VARCHAR(50),
  target_id       VARCHAR(50),
  meta            JSON,
  created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

### Seed inicial de planos

| slug | max_active_leads | max_add/dia | exportação |
|------|------------------|-------------|------------|
| `trial` | 5 | 10 | ❌ |
| `starter` | 20 | 30 | 5/dia |
| `pro` | 50 | 100 | 20/dia |

---

## 4. Especificação completa da API LeadB2B

**Base:** `https://leadb2b.org/api/v1`  
**Auth:** `Authorization: Bearer {token}` + cookie HttpOnly `leadb2b_session` (recomendado para SSR futuro)

### 4.1 Autenticação e conta

| Método | Endpoint | Auth | Descrição |
|--------|----------|------|-----------|
| POST | `/auth/register` | Público | Cria conta + plano trial + sessão |
| POST | `/auth/login` | Público | Login; proxy opcional para Global API JWT |
| POST | `/auth/logout` | Bearer | Encerra sessão |
| POST | `/auth/forgot-password` | Público | Envia e-mail de reset |
| POST | `/auth/reset-password` | Token reset | Nova senha |
| GET | `/auth/me` | Bearer | Perfil + plano + limites + uso do dia |
| PATCH | `/auth/me` | Bearer | Atualiza nome, etc. |
| PATCH | `/auth/me/password` | Bearer | Troca senha |

**`GET /auth/me` — resposta esperada (coração do produto):**

```json
{
  "user": { "id": 1, "email": "...", "name": "...", "role": "user" },
  "subscription": { "plan": "trial", "status": "trial", "ends_at": "..." },
  "limits": {
    "max_active_leads": 5,
    "active_leads_count": 3,
    "slots_remaining": 2,
    "max_add_per_day": 10,
    "added_today": 4,
    "can_export": false,
    "exports_today": 0,
    "max_export_per_day": 0
  },
  "access": { "can_use_app": true, "reason": null }
}
```

**Regra:** se `subscription.status` ∉ `{trial, active}` → `can_use_app: false` → 403 em rotas de produto.

---

### 4.2 Busca de empresas (proxy controlado)

| Método | Endpoint | Auth | Descrição |
|--------|----------|------|-----------|
| GET | `/search/filters` | Bearer | Metadados (proxy `/leads/filtros`) |
| GET | `/search/municipios` | Bearer | Proxy com cache |
| GET | `/search/cnaes` | Bearer | Proxy com cache |
| GET | `/search/companies` | Bearer | Listagem **mascarada** |
| GET | `/search/companies/{cnpj}` | Bearer | Preview **sem contatos completos** |
| GET | `/search/companies/{cnpj}/full` | Bearer | Só se lead já está em `user_leads` |

**Regras anti-extração na listagem (`/search/companies`):**

- Paginação obrigatória: `page_size` máx 20 (trial: 10)
- Retornar: nome, CNPJ parcial (`**.0001/0001-**`), cidade, UF, CNAE resumido, badges booleanos (`has_phone`, `has_whatsapp`, `has_email`) — **sem números/e-mails**
- Marcar `already_working: true` se CNPJ já está no funil
- Rate limit por usuário: 60 req/min

**Filtros MVP suportados** (repasse para Global API):

`uf`, `municipio`, `cnae_fiscal_principal`, `tem_telefone`, `tem_email`, `q`, `cursor`, `page_size`

**WhatsApp MVP:** derivar de `tem_telefone=true` + flag local `assumed_whatsapp` (celular DDD) ou pedir filtro na Global API depois.

---

### 4.3 Funil — “Trabalhar lead” (ação central)

| Método | Endpoint | Auth | Descrição |
|--------|----------|------|-----------|
| POST | `/leads/work` | Bearer | Adiciona empresa ao funil |
| GET | `/leads` | Bearer | Lista leads (filtro por stage) |
| GET | `/leads/{id}` | Bearer | Detalhe completo (busca Global API + metadados locais) |
| PATCH | `/leads/{id}/stage` | Bearer | Move no kanban |
| PATCH | `/leads/{id}` | Bearer | Notas |
| DELETE | `/leads/{id}` | Bearer | Só se stage = arquivado (opcional) |

**`POST /leads/work` — body:**

```json
{ "cnpj": "07367073000165" }
```

**Processo interno (obrigatório):**

1. Verificar `can_use_app`
2. Contar leads ativos: `stage NOT IN (fechado, perdido, arquivado)`
3. Se `active >= max_active_leads` → **409 `ACTIVE_LIMIT_REACHED`**
4. Se `added_today >= max_add_per_day` → **429 `DAILY_ADD_LIMIT`**
5. Se CNPJ já existe → **409 `ALREADY_WORKING`**
6. Buscar snapshot na Global API (`GET /estabelecimentos/{cnpj}`)
7. Inserir em `user_leads` com `stage = novos`
8. Incrementar `daily_usage.leads_added`
9. Registrar `lead_stage_history`
10. Retornar lead completo com contatos desbloqueados

**Stages terminais** (`fechado`, `perdido`, `arquivado`) **liberam slot** imediatamente.

---

### 4.4 Kanban

| Método | Endpoint | Auth | Descrição |
|--------|----------|------|-----------|
| GET | `/kanban` | Bearer | Board completo agrupado por coluna |
| GET | `/kanban/stats` | Bearer | Contadores por coluna + slots |

**`GET /kanban` — resposta:**

```json
{
  "columns": {
    "novos": [],
    "contatando": [],
    "respondeu": [],
    "negociacao": [],
    "fechado": [],
    "perdido": [],
    "arquivado": []
  },
  "meta": { "active_count": 3, "slots_remaining": 2 }
}
```

Cards leves: `id`, `cnpj`, `company_name`, `city`, `uf`, `has_phone`, `worked_at`, `stage_changed_at`.

---

### 4.5 Ações de contato

| Método | Endpoint | Auth | Descrição |
|--------|----------|------|-----------|
| POST | `/leads/{id}/contact/whatsapp` | Bearer | Retorna URL wa.me + mensagem renderizada |
| POST | `/leads/{id}/contact/email` | Bearer | Retorna mailto ou copy payload |
| GET | `/leads/{id}/contact/options` | Bearer | Telefones/e-mails disponíveis |

**`POST /leads/{id}/contact/whatsapp` — body:**

```json
{ "template_id": 1, "phone_index": 0 }
```

**Resposta:**

```json
{
  "url": "https://wa.me/5511999999999?text=Olá%20Empresa%20XYZ...",
  "message": "Olá Empresa XYZ, ...",
  "phone": "(11) 99999-9999"
}
```

Registra `contact_events` para métricas. **Não envia automaticamente** (MVP).

---

### 4.6 Mensagens padrão

| Método | Endpoint | Auth |
|--------|----------|------|
| GET | `/templates` | Bearer |
| POST | `/templates` | Bearer |
| PATCH | `/templates/{id}` | Bearer |
| DELETE | `/templates/{id}` | Bearer |

Variáveis MVP: `{{empresa}}`, `{{cidade}}`, `{{uf}}`.

---

### 4.7 Exportação controlada

| Método | Endpoint | Auth |
|--------|----------|------|
| POST | `/exports` | Bearer |
| GET | `/exports` | Bearer |
| GET | `/exports/{id}/download` | Bearer |

**Regras:**

- Só leads com `worked_at` preenchido
- Só stages ≠ `novos` (opcional: exigir ao menos 1 `contact_event`)
- Respeitar `can_export` e `max_export_per_day`
- Máximo 100 linhas por job no MVP

---

### 4.8 Indicadores (dashboard)

| Método | Endpoint | Auth |
|--------|----------|------|
| GET | `/stats/overview` | Bearer |

```json
{
  "active_leads": 3,
  "finished_leads": 12,
  "slots_remaining": 2,
  "added_today": 4,
  "contacts_today": 7,
  "conversion_rate": 0.15
}
```

---

### 4.9 Admin (role = admin)

| Método | Endpoint | Descrição |
|--------|----------|-----------|
| GET | `/admin/users` | Lista usuários |
| GET | `/admin/users/{id}` | Detalhe + uso |
| PATCH | `/admin/users/{id}/status` | Ativar/suspender |
| PATCH | `/admin/users/{id}/subscription` | Trocar plano manualmente |
| GET | `/admin/plans` | CRUD planos |
| POST/PATCH | `/admin/plans` | Gerenciar planos |
| GET | `/admin/metrics` | Usuários ativos, leads/dia, exportações |
| GET | `/admin/audit` | Log de ações |

---

### 4.10 Códigos de erro padronizados

| HTTP | Code | Quando |
|------|------|--------|
| 401 | `UNAUTHORIZED` | Token inválido |
| 403 | `PLAN_INACTIVE` | Plano expirado |
| 403 | `FEATURE_DISABLED` | Export no trial |
| 409 | `ACTIVE_LIMIT_REACHED` | Funil cheio |
| 409 | `ALREADY_WORKING` | CNPJ duplicado |
| 422 | `INVALID_CNPJ` | CNPJ inválido |
| 429 | `DAILY_ADD_LIMIT` | Limite diário |
| 429 | `RATE_LIMIT` | Muitas requisições |

---

## 5. Mapeamento páginas → API

| Página atual | Rota futura | Endpoints |
|--------------|-------------|-----------|
| `cadastro.html` | `/cadastro` | `POST /auth/register` |
| `login.html` | `/login` | `POST /auth/login` |
| `dash-senha.html` | `/app/senha` | `PATCH /auth/me/password`, `POST /auth/forgot-password` |
| `busca` (logado) | `/app/busca` | `GET /search/*` |
| *(nova)* | `/app/kanban` | `GET /kanban`, `PATCH /leads/{id}/stage` |
| `dash-favoritos.html` | **vira Kanban** | substituir favoritos por funil |
| `dashboard.html` | `/app` | `GET /auth/me`, `GET /stats/overview` |
| `dash-pacote.html` | `/app/plano` | `GET /auth/me` (limits) |
| `dash-exportacoes.html` | `/app/exportacoes` | `GET/POST /exports` |
| `dash-transacoes.html` | `/app/transacoes` | futuro gateway pagamento |
| `dash-perfil.html` | `/app/perfil` | `GET/PATCH /auth/me` |
| `empresa.html` (logado) | modal/drawer | `POST /leads/work`, contact endpoints |
| `pricing.html` | `/pricing` | estático + redirect pós-login |
| **Admin** *(novo)* | `/admin/*` | `/admin/*` |

### Sidebar do dashboard — revisão proposta

```
Dashboard (indicadores)
Buscar empresas
Kanban ← NOVO (centro do produto)
Meu plano
Exportações
Mensagens padrão ← NOVO
Perfil / Senha
Sair
```

---

## 6. Processos críticos (não podem falhar)

### Processo A — Primeiro acesso

```
Cadastro → POST /auth/register
  → cria user + subscription trial
  → retorna token + redirect /app/busca
  → banner: "Você tem 5 slots ativos. Trabalhe leads para liberar mais."
```

### Processo B — Ciclo diário (produto)

```mermaid
sequenceDiagram
    participant U as Usuário
    participant API as LeadB2B API
    participant G as Global API

    U->>API: GET /search/companies?uf=SP&tem_telefone=true
    API->>G: proxy (API Key servidor)
    G-->>API: empresas
    API-->>U: listagem mascarada

    U->>API: POST /leads/work {cnpj}
    API->>API: valida limites
    API->>G: GET /estabelecimentos/{cnpj}
    API->>API: INSERT user_leads stage=novos
    API-->>U: lead completo

    U->>API: POST /leads/{id}/contact/whatsapp
    API-->>U: URL wa.me

    U->>API: PATCH /leads/{id}/stage {stage: contatando}

    U->>API: PATCH /leads/{id}/stage {stage: fechado}
    API->>API: slot liberado (+1 slots_remaining)
```

### Processo C — Bloqueio anti-extração

Toda rota de busca logada passa por:

1. `RequireActivePlan`
2. `MaskCompanyData(unless_in_user_leads)`
3. `EnforcePageSize(plan)`
4. `LogSearchQuery(user_id, filters)` — auditoria

Busca **pública** (`/busca` SEO) pode continuar como hoje para indexação, mas **sem contatos** na listagem (só badges) — alinhado ao MVP.

---

## 7. O que fica na Global API vs LeadB2B

| Responsabilidade | Onde |
|------------------|------|
| Dados RFB (CNPJ, sócios, endereço) | Global API |
| Auth e-mail/senha (opcional proxy) | Global API `/auth/*` |
| Rate limit de consulta RFB | Global API (API Key do servidor) |
| Plano comercial LeadB2B | **LeadB2B DB** |
| Leads ativos / kanban | **LeadB2B DB** |
| Templates de mensagem | **LeadB2B DB** |
| Export controlado | **LeadB2B DB** |
| Admin do produto | **LeadB2B API** |
| Pagamento (futuro) | Gateway + **LeadB2B DB** |

---

## 8. Modelo formal de solicitação de construção da API

### SOLICITAÇÃO DE CONSTRUÇÃO — API LeadB2B MVP

**Projeto:** leadb2b.org  
**Objetivo:** Sistema de prospecção guiada com funil kanban, limites por plano e anti-extração  
**Stack:** Go (chi) + MySQL `leadb2b_app` + proxy Global Leads API

#### Escopo funcional

- [ ] Autenticação completa (register, login, logout, forgot/reset password)
- [ ] Middleware: plano ativo obrigatório para rotas `/search/*`, `/leads/*`, `/kanban/*`
- [ ] CRUD de planos + seed trial/starter/pro
- [ ] Busca proxy com mascaramento de contatos
- [ ] Ação `POST /leads/work` com enforcement de limites
- [ ] Kanban 7 colunas com movimentação
- [ ] Contato WhatsApp (URL) e e-mail (copy/mailto)
- [ ] Templates de mensagem com variáveis
- [ ] Exportação limitada por plano
- [ ] Dashboard stats
- [ ] Admin básico (usuários, planos, métricas)
- [ ] Testes de integração nos fluxos A, B e C

#### Entregáveis técnicos

1. Migrações SQL (`migrations/001_initial.sql`)
2. Pacote `internal/store` (repositórios MySQL)
3. Pacote `internal/auth` (JWT/sessão)
4. Pacote `internal/product` (leads, kanban, limits)
5. Rotas `/api/v1/*` registradas em `cmd/server/main.go`
6. OpenAPI spec (`docs/openapi.yaml`)
7. Variáveis `.env`: `APP_DB_DSN`, `JWT_SECRET`, `GLOBALAPI_*` (já existem)

#### Critérios de aceite (MVP)

1. Usuário trial não consegue adicionar 6º lead ativo com 5 no funil
2. Mover lead para `fechado` libera slot imediatamente
3. Listagem de busca não expõe telefone/e-mail antes de `POST /leads/work`
4. Trial não consegue exportar (403)
5. `GET /auth/me` reflete uso real em tempo real
6. Kanban carrega em < 500ms com até 50 cards totais

#### Fases de implementação

| Fase | Escopo | Duração est. |
|------|--------|--------------|
| **F1 — Fundação** | DB, auth, `/auth/me`, middleware plano | 1–2 sem |
| **F2 — Funil** | search mascarado, `work`, kanban, stats | 2 sem |
| **F3 — Contato** | templates, whatsapp/email, export | 1 sem |
| **F4 — Admin + polish** | admin API, front integrado, testes | 1–2 sem |

#### Fora do escopo MVP

- Pagamento automático (Stripe/Asaas)
- Envio de e-mail pela plataforma
- IA para variações de mensagem
- App mobile
- Multi-usuário por conta (times)

#### Dependências externas

- Global Leads API operacional (`leads.globals.tech`)
- MySQL disponível no servidor (pode ser mesmo host de `globals_rfempresas`, banco separado)
- SMTP para reset de senha
- (Opcional) Decisão: auth própria vs proxy JWT Global API

---

## 9. Recomendações imediatas antes de codar

1. **Criar banco `leadb2b_app`** no mesmo MySQL do servidor — não misturar com `globals_rfempresas`.
2. **Decidir auth:** se a Global API já gerencia contas CRM, o register/login pode ser proxy; o LeadB2B guarda só `global_user_id` + dados de produto. Se quiser independência comercial, auth própria com Global API só para dados RFB.
3. **Registrar rotas do dashboard** no Go ou migrar para templates Go com JS consumindo `/api/v1`.
4. **Renomear `dash-favoritos` → Kanban** — favoritos não fazem parte do MVP; o funil substitui.
5. **Proteger busca logada** — a busca pública SEO pode permanecer aberta; o produto real exige login.
6. **Nunca expor `GLOBALAPI_KEY` no browser** — toda chamada RFB passa pelo backend LeadB2B.

---

## 10. Visão do MVP (regra principal)

Sistema de prospecção diária com leads integrados, onde o usuário é limitado a trabalhar um número controlado de empresas por vez.

### Objetivo do produto

Substituir compra de listas por execução guiada. Fazer o usuário entrar todo dia, trabalhar leads, avançar no funil. Impedir uso como “extração de base de dados”.

### Princípios do MVP (não negociáveis)

- Leads **não** são ilimitados
- Usuário precisa trabalhar para liberar mais
- A plataforma guia a ação (não só entrega dados)
- Tudo gira em torno do **Kanban**
- A ação principal é contato (WhatsApp/e-mail)

### Erros que não podem acontecer

- Liberar exportação total
- Não limitar leads ativos
- Não ter Kanban claro
- Não facilitar contato
- Interface complicada

### Resumo em uma frase

> Um sistema onde o usuário busca empresas, adiciona ao funil, entra em contato e só consegue novos leads quando termina os atuais.

---

## 11. Resumo executivo

Hoje o LeadB2B é um **site de consulta RFB** com protótipos visuais de conta/dashboard, mas **zero backend de produto**. A Global Leads API cobre dados e auth CRM; **toda a lógica de funil, limites, kanban e anti-extração precisa nascer na API LeadB2B** com banco próprio.

A frase do MVP se traduz em **uma regra de API**:

> O usuário só recebe dados completos e novos slots quando **finaliza leads ativos** no kanban — enforced em `POST /leads/work` via contagem de `stage NOT IN (fechado, perdido, arquivado)`.
