Arquitetura da Plataforma
O Switchbord é implantado como uma aplicação Next.js multi-tenant na Vercel, com o Supabase como base para persistência, autenticação e realtime, e um worker hospedado na Railway para processamento em segundo plano. Não existe um serviço de control-plane separado — esse caminho arquitetural foi descontinuado e todas as responsabilidades de runtime foram consolidadas.Topologia
apps/app: produto do operador — caixa de entrada, contatos, templates, campanhas, Margaret AI, configurações. Implantado na Vercel.apps/web: site de marketing, fluxos de QR/opt-in, landing pages restritas. Implantado na Vercel.apps/api: superfície de API Next.js para ingestão de webhook da Meta, API REST pública v1 (especificação OpenAPI em/spec), endpoints de compatibilidade e rotas de health/status. Implantado na Vercel.apps/docs: documentação baseada em Mintlify. Implantado no Mintlify.apps/worker: processamento de queue, retries, agendadores de campanha, despacho de mensagens, ferramentas de replay. Implantado na Railway.- Supabase: Postgres, Auth, Storage, Realtime, Vault. Fonte única de verdade.
O serviço
apps/control-plane foi removido. Todo o trabalho de control-plane orientado a webhook agora é executado dentro de apps/api e apps/worker. Quaisquer referências locais à porta 4000 ou a apps/control-plane/.env.example são artefatos legados.Caminhos Principais de Runtime
- Webhook de entrada — a Meta entrega um webhook para
apps/api→ assinatura verificada comcrypto.timingSafeEqual→ envelope bruto armazenado emwebhook_events→ o Supabase notifica oapps/worker→ o worker roteia para o processador de mensagem, status, template ou jornada. - Envio de saída — o operador ou uma automação cria uma intenção de envio → o Supabase registra em
outbox_jobs→apps/workeragenda considerando limites de throughput/pares → a resposta da Meta é registrada → webhooks de status atualizam o ledger. - Disparos em massa — um snapshot de audiência é criado → o worker distribui os jobs de outbox → o Supabase Realtime envia o progresso da campanha ao dashboard do operador.
- Margaret AI — o operador aciona um rascunho de resposta de IA →
apps/appchama/api/ai-assistant→ o provider de LLM do workspace é resolvido a partir dellm_provider_configs→ PII é pseudonimizado antes de qualquer prompt → a resposta é despseudonimizada → o rascunho aparece na caixa de entrada. A execução de agentes (BORD-140–143) adiciona uso de ferramentas (tool-use) sobre isso.
Modelo de Workspace Multi-Tenant
Cada tenant é um workspace isolado. As garantias centrais de isolamento são:- O contexto do workspace é resolvido no lado do servidor a partir de
workspace_members— nunca a partir de variáveis de ambiente ou headers fornecidos pelo cliente. - Todas as consultas de dados são delimitadas por
workspace_id, derivado dessa resolução. - O despacho de mensagens lê as credenciais da Meta a partir dos segredos do Vault pertencentes ao workspace proprietário — sem vazamento de credenciais entre workspaces.
- Quatro papéis de RBAC aplicados em toda rota: owner, admin, developer, operator.
Camada Margaret AI
A camada de IA introduz três novas preocupações de runtime:- Configurações de provider de LLM — registros pertencentes ao workspace em
llm_provider_configs, apontando para OpenAI, Anthropic, OpenRouter, Ollama ou vLLM. Chaves de API armazenadas no Supabase Vault viaworkspace_secret_bindings. - Agentes de IA — a tabela
ai_agentsdefine persona, modelo, permissões de ferramentas e configurações de pseudonimização de PII por workspace. - Threads de IA —
ai_threadseai_thread_messagesmantêm o contexto de conversa para as respostas da Margaret.ai_agent_executionsregistra execuções de uso de ferramentas para fins de auditoria.
Ledger de Consentimento
O ledger de consentimento (BORD-193) fornece um registro durável e auditável de todo evento de opt-in e opt-out por contato:- Tabela
consent_events— log somente de inserção (append-only) das transições de consentimento (opt-in / opt-out / revogado). Um triggerAFTER INSERTnessa tabela desnormaliza o estado mais recente emcontacts.consent_statepara leituras rápidas. - Detecção de palavras-chave STOP / START —
packages/whatsappanalisa mensagens de entrada em busca de palavras-chave localizadas de opt-out (STOP,BLOQUEAR,FERMA,PARAR) e opt-in (START,INICIO,INIZIO,COMEÇAR) em inglês, italiano, português e espanhol. - Gate de opt-out no despacho de campanhas — o motor de disparos em massa verifica
contacts.consent_stateantes de enfileirar qualquer job de outbox. Contatos em estadoopted_outourevokedsão ignorados silenciosamente. - Endpoints REST —
GET /api/v1/contacts/:id/consentretorna o estado atual e o histórico de eventos;POST /api/v1/contacts/:id/consentregistra uma transição de consentimento manual (apenas owner/admin).
Command Palette
O command palette (BORD-204) é uma superfície ⌘K para navegação, busca e ações em todo o produto do operador:command-palette.tsx— o shell da paleta, acionado por ⌘K / Ctrl+K. Renderiza uma lista pesquisável de comandos agrupados por categoria.command-palette-provider.tsx— contexto React que agrega os comandos disponíveis de cada módulo (caixa de entrada, contatos, campanhas, templates, jornadas, configurações) e gerencia o estado aberto/fechado.command-palette-hint.tsx— badge de dica inline renderizado na barra lateral e nos cabeçalhos de página para ajudar a descobrir o atalho.
registerCommands do provider.
Editor Gráfico de Jornadas
Um canvas React Flow em/journeys/[id] para construir automações multi-etapas visualmente:
- 9 tipos de node — Trigger, Condition, Wait, Send Template, Update Attribute, Add/Remove Tag, Outbound Webhook, AI Agent Step e Exit.
- Validação — o editor valida campos obrigatórios, conectividade de edges e detecção de ciclos antes de publicar. Erros aparecem inline em cada node.
- Pipeline de publicação — os grafos validados são serializados na tabela
journeyscomo JSON versionado. Uma mutationpublishcria um snapshot imutável e ativa a jornada para execução em tempo real peloapps/worker.
Construtor Visual de Templates
Um construtor de 3 painéis em/templates/new para compor templates de mensagem do WhatsApp:
- 7 editores — Header, Body, Footer, Buttons, Examples, Language e Category.
- Coluna
parameter_format— armazenada emmessage_templatespara declarar o tipo e a ordem esperados das variáveis do template. - Pipeline de submissão à Meta — o construtor faz um POST do template composto para a Meta Business API e acompanha o status de aprovação por meio de webhooks de status de template.
- Renderizador em phone-frame — um componente de preview em tempo real renderiza o template como ele apareceria em um dispositivo, com substituição de placeholders pelas variáveis.
Agente de Auto-Trigger
O agente de auto-trigger (BORD-192) estende a Margaret AI para redigir respostas automaticamente em mensagens de WhatsApp de entrada:- Gatilho de execução do agente — quando uma mensagem de entrada chega e o workspace tem um agente de auto-trigger configurado,
agent.runé invocado automaticamente pelo worker. - API de aceitar/rejeitar rascunho —
POST /api/v1/contacts/:id/drafts/:draftId/acceptePOST /api/v1/contacts/:id/drafts/:draftId/rejectpermitem que operadores aprovem ou descartem rascunhos gerados por IA. - Banner de rascunho na caixa de entrada — um banner visível na visualização da conversa avisa os operadores quando um rascunho está pendente, com ações de aceitar/rejeitar em um clique.
Armazenamento de Assets
O armazenamento de assets (BORD-187) fornece uploads de arquivos delimitados por workspace para templates, jornadas e assets de conhecimento:- Tabela
asset_files— registra metadados do arquivo (nome, tipo MIME, tamanho, escopo de workspace) e aponta para o objeto armazenado. - Pipeline compatível com S3 — os uploads passam por um pipeline de URL pré-assinada para qualquer object store compatível com S3. O worker recupera os objetos no momento do despacho para envios de mídia.
- Isolamento de workspace — todo arquivo é delimitado por
workspace_id; o acesso entre workspaces é rejeitado na camada de API.
Alertas de Conta
Os alertas de conta (BORD-189) exibem para os operadores problemas em nível de conta da Meta:- Handler de webhook —
apps/apiprocessa eventos de webhookaccount_alertsda Meta e os armazena emaccount_alert_events. - Painel de UI do operador — um painel dedicado nas configurações exibe alertas ativos (por exemplo, violações de política, avisos de rate limit, expiração de credencial) com severidade e passos de resolução.
Logs de Auditoria
Os logs de auditoria (BORD-158) registram ações do operador para conformidade e forense:- Tabela
audit_logs— log somente de inserção (append-only), delimitado por workspace, de ator, ação, recurso alvo e payload de diff. writeAuditLog()— um helper do lado do servidor que toda rota de mutation chama antes de fazer commit. Garante estrutura consistente e evita omissões acidentais.
Retenção de Dados LGPD/GDPR
A retenção de dados LGPD/GDPR (BORD-165 / BORD-167) impõe limites de retenção por workspace:data_retention_days— uma configuração em nível de workspace (padrão 365). Controla por quanto tempo envelopes de webhook, conteúdo de mensagem e eventos de consentimento são retidos.- Job
retention_sweep— um job agendado do worker que exclui registros mais antigos que a janela de retenção do workspace. Executa diariamente; registra métricas de limpeza emaudit_logs.
Geração de Chave de API
A geração de chave de API (BORD-178) permite que owners de workspace provisionem chaves de API de longa duração:- Chaves com prefixo
swb_— geradas no servidor com um segredo criptograficamente aleatório de 32 bytes. O prefixo permite identificação visual rápida e detecção programática em logs. - Metadados de chave — armazenados em
api_keyscom timestamp de criação, timestamp de último uso e descrição opcional. As chaves são armazenadas com hash em repouso; o segredo completo é exibido apenas uma vez, no momento da criação.
Wizard Guiado
O wizard guiado (BORD-194) fornece um fluxo de onboarding do primeiro acesso até a primeira mensagem:- Sequência de etapas — conectar número de WhatsApp → configurar provider de LLM → criar o primeiro template → enviar a primeira mensagem.
- Rastreamento de progresso — armazenado em
workspace_onboarding_state. O wizard avança automaticamente e pode ser dispensado após a conclusão. - Ponto de entrada — workspaces recém-criados são redirecionados para
/welcomeno primeiro login. O wizard também está acessível a partir de Configurações.
Primitivas do Design System
Novas primitivas de design system lançadas empackages/design-system:
- Tokens de design — tokens semânticos de cor, espaçamento e tipografia em
tokens.css, consumidos por todos os componentes shadcn/ui. - Motion — utilitários consistentes de transição e animação em
motion.ts(durações, easings, presets de entrada/saída). - Skeleton — componente
Skeletoncom animação de pulso, usado como placeholder de carregamento em todo o produto do operador.
Atualizações de Marca
- Wordmark transparente — o logo da barra lateral expandida agora usa um wordmark SVG com fundo transparente para renderização limpa em qualquer superfície.
- Favicon com fundo preto —
favicon.icoatualizado para uma variante com fundo preto para maior visibilidade nas abas do navegador.
Princípios de Dados e Segurança
- Todo envelope de webhook é imutável; corpo bruto + metadados são retidos com controles de retenção configuráveis.
- O histórico de entrega é somente de inserção (append-only) (
outbox_jobs,message_status_events) — nunca sobrescrito. - RBAC, feature flags e segredos de provider permanecem exclusivamente no lado do servidor.
- Tópicos privados de Supabase Realtime Broadcast alimentam os feeds em tempo real do operador; nenhum Postgres Changes broadcast é usado, para evitar problemas de escalabilidade.
- Log de auditoria em toda ação do operador e rotação de credencial.
- O ledger de consentimento impõe opt-out antes de qualquer despacho de campanha ou automação.
- As limpezas de retenção de dados impõem os prazos de exclusão da LGPD/GDPR por workspace.
UI do Operador — Barra Lateral e Navegação
A UI do operador (apps/app) usa uma barra lateral construída sobre as primitivas Sidebar do shadcn/ui.
Componente
- Arquivo:
apps/app/components/operator-sidebar.tsx(~165 linhas) - Padrão:
collapsible='icon'— recolhe para um trilho somente com ícones; expande ao passar o mouse ou ao alternar. A interface de props é estável. - Layout: cada página que precisa da barra lateral envolve seu conteúdo com
SidebarProvider.
Estrutura de navegação
- Seção superior (nav plana): Caixa de entrada / Contatos / Disparos em massa / Templates / Operações
- Seção inferior (rodapé): Configurações / Docs / Conta do usuário
Tema
A barra lateral usa os valores padrão de variáveis CSS claras do shadcn — não há tema escuro customizado ouclassName='dark' aplicado ao elemento da barra lateral.
Variáveis relevantes (definidas em :root em packages/design-system/styles/globals.css):
--sidebar: fundo quase branco--sidebar-foreground: texto quase preto
Logo
- Expandida: wordmark completo
- Recolhida: marca somente com ícone
Monitoramento + Deployment
- Endpoints de health:
/health,/ready,/internal/runtime. - Endpoints operacionais:
/internal/webhooks,/internal/webhook-rejections,/internal/webhooks/:id/replay. - Especificação OpenAPI + explorador Scalar:
/spec. - Deploy de
apps/app,apps/web,apps/apieapps/docsna Vercel/Mintlify. Deploy deapps/workerna Railway. Todas as superfícies compartilham o mesmo projeto Supabase. - O CI valida builds, migrations, lint de docs e verificações de segredos. Veja CI/CD.