Skip to content

Schema do Banco de Dados

Documentação de todos os modelos Prisma do sistema. Fonte: backend/prisma/schema.prisma.

Diagrama de domínios

Organization (raiz de tudo)
  ├── Store[] (lojas)
  │     ├── StoreWhatsappNumber[] (números WhatsApp por loja)
  │     │     └── WhatsappTemplate[]
  │     ├── EmailSettings
  │     ├── Seller[]
  │     └── Customer[] (clientes da loja)

  ├── Customer[] (clientes da org)
  │     ├── Transaction[] (compras)
  │     ├── CustomerEvent[]
  │     ├── RfmHistory[]
  │     ├── CampaignRedirectToken[]
  │     └── PostSaleSurveyJob[]

  ├── Campaign[]
  │     ├── CampaignContent[]
  │     ├── CampaignMetric
  │     ├── CampaignSchedule[]
  │     ├── WhatsappMessage[]
  │     ├── EmailMessage[]
  │     └── CampaignRedirectToken[]

  ├── Segment[]
  ├── Role[]
  ├── User[]
  ├── AccessLog[]
  ├── Conversation[]
  │     └── Message[]
  ├── ChatwootConfig
  ├── EmailTemplate[]
  ├── OrganizationSmtpSettings
  ├── PostSaleSurveyConfig[]
  └── SurveyResponse[]

Organization

Raiz de toda a hierarquia multi-tenant. Cada organização é um cliente do CRM.

CampoTipoDescrição
idUUIDPK
nameStringNome da organização
clerkOrgIdString?ID no Clerk (único)
syncStatusStringPENDING | SYNCING | COMPLETED | ERROR
lastSyncDateDateTime?Última vez que o ERP sync completou
lastCustomerTransIdBigIntMaior trans_id já sincronizado (sync incremental)
campaignSettingsJson?{ replyWindowHours: 48 }
logoUrlString?URL do logo

Store

Representa uma filial física ou e-commerce.

CampoTipoDescrição
idUUIDPK
codeString?Código da filial no ERP (único)
nameStringNome da loja
tradeNameString?Nome fantasia
cnpj, ieString?Documentos fiscais
email, phoneString?Contato da filial
cep, street, number, neighborhood, city, stateString?Endereço
isEcommerceBooleanMarca a filial de e-commerce (fallback de redirecionamento)
primaryStoreIdString?FK para loja principal (alias — ver Regras de Lojas)
businessHoursJson?Horário de funcionamento por dia da semana
settingsJson?Configurações extras
isActiveBooleanSe está ativa
agentCredentialEmailString?Email para receber credenciais Chatwoot

Índices: organizationId


BusinessHoursWaiting

Clientes que enviaram mensagem fora do horário comercial.

CampoTipoDescrição
idCUIDPK
phoneStringTelefone do cliente
storeId, organizationIdStringTenant
extraNoteString?Nota extra para o atendente

Unique: [phone, storeId]


User

Usuários do CRM (funcionários, admins). Autenticados via Clerk.

CampoTipoDescrição
idUUIDPK
clerkIdString?ID no Clerk
clerkInvitationIdString?ID do convite pendente
emailStringÚnico
nameString?Nome
statusStringACTIVE | INVITED
roleIdString?FK → Role
organizationIdStringFK → Organization
accessibleStoresStore[]Lojas visíveis (vazio = todas)

Role

Papel de usuário com permissões e nível hierárquico.

CampoTipoDescrição
idUUIDPK
nameStringEx: "Super Admin", "Vendedor"
levelInt0 = Super Admin, maior = menos poder
permissionsString[]Ex: ["app:campaigns", "app:customers"]
readOnlyBooleanSe true, só GET
organizationIdStringFK → Organization

AccessLog

Registro de auditoria de ações sensíveis.

CampoTipoDescrição
idUUIDPK
userId, organizationIdStringQuem fez
storeIdString?Em qual loja
endpoint, methodStringHTTP
actionTypeStringEx: CAMPAIGN_SENT, USER_INVITED
ip, userAgentString?Origem (IP mascarado)
metadataJson?IDs relevantes da ação
statusInt?HTTP status

Índices: userId, actionType, createdAt, organizationId


Customer

Cliente sincronizado do ERP Millennium.

CampoTipoDescrição
idUUIDPK
externalIdString?ID no ERP
transIdBigInt?trans_id do Millennium (sync incremental)
name, email, phoneString?PII
phoneTypePhoneTypeMOBILE | LANDLINE | INVALID | UNKNOWN
cpfHashString?SHA-256 do CPF (busca/dedup)
cpfEncryptedString?AES-256-GCM (uso interno)
cpfMaskedString?***.***.XXX-** (display)
cpfValidBoolean?Validez do CPF
birthDateDateTime?Data de nascimento
zipCode, city, state, address, neighborhoodString?Endereço
personTypeStringPF | PJ
rfmStatusString?Segmento atual (ex: champions)
previousRfmStatusString?Segmento anterior
tagsString[]Labels livres
totalSpentFloatTotal gasto
orderCountIntNúmero de pedidos
lastOrderDateTime?Data da última compra
storeIdStringLoja principal
preferredStoreIdString?Loja preferida para roteamento WhatsApp
preferredSellerIdString?Vendedor preferido
sellerAssignmentCycleJson?Estado do round-robin por loja
whatsappOptOutBooleanSe descadastrou de WhatsApp
emailOptOutBooleanSe descadastrou de email
smsOptOutBooleanSe descadastrou de SMS
whatsappUnsubscribeTokenString?Token permanente para link de descadastro
dataQualityIssuesJson?Problemas de qualidade identificados

Uniques: [storeId, externalId], cpfHash

Índices: organizationId, [organizationId, lastOrder], email, phone, tags


CustomerEvent

Eventos de comportamento do cliente (ex: abertura de email, clique em link).

CampoTipoDescrição
customerIdStringFK → Customer
typeStringTipo do evento
dateDateTimeQuando ocorreu
metadataJson?Dados extras

RfmHistory

Histórico de segmentação RFM do cliente.

CampoTipoDescrição
customerIdStringFK → Customer
scoreIntScore calculado
segmentStringSegmento (ex: champions)
dateDateTimeData do cálculo

Product

Produtos sincronizados do ERP.

CampoTipoDescrição
idUUIDPK
externalIdStringID numérico do ERP (único)
productCodeString?Código zero-padded (join key com Transaction.items)
nameStringdescricao1 do ERP
shortNameString?descricao2
collection, department, group, brand, category, typeString?Taxonomia
colors, sizes, patternsString[]Variações
priceFloat?Preço
statusStringACTIVE | INACTIVE

Índices: productCode, collection, department, group, division, brand, category, type


Seller

Vendedores sincronizados do ERP.

CampoTipoDescrição
idUUIDPK
externalIdStringID no ERP (único)
codFuncionarioString?Código formatado (ex: 000246)
name, email, phoneString?Dados do funcionário
cpfHash, cpfEncrypted, cpfMaskedString?CPF protegido
storeIdString?Loja atual
branchCodeString?cod_filial do ERP

Transaction

Compras sincronizadas do ERP.

CampoTipoDescrição
idUUIDPK
externalIdString?ID no ERP (único)
totalValueFloatValor total
dateDateTimeData da compra
statusStringPAID | CANCELLED
channelString?Canal de venda
isInfluencedBooleanSe foi atribuída a uma campanha
campaignIdString?FK → Campaign (atribuição)
salespersonIdString?FK → Seller
vendorCode, vendorNameString?Vendedor raw do ERP
itemsJson?Array de produtos (id, qty, price)
itemCountInt?Total de peças

Índices: date, customerId, externalId, vendorCode, campaignId, [status, date], [customerId, status, date], [storeId, date]


Segment

Segmento de clientes com regras dinâmicas.

CampoTipoDescrição
idUUIDPK
name, descriptionString
rules, conditionsJson?Filtros (ver Módulo CRM)
logicStringcustom | rfm
isDynamicBooleanSe recalcula automaticamente
lastCountIntÚltima contagem
storeIdString?Filtrado por loja

Campaign

Campanha de comunicação (WhatsApp ou Email).

CampoTipoDescrição
idUUIDPK
nameStringNome da campanha
typeStringemail | whatsapp
channelStringEmail | WHATSAPP
statusStringdraft | scheduled | sending | sent | cancelled
scheduledAtDateTime?Agendamento
sentAtDateTime?Quando foi enviada
sent, delivered, opens, clicksIntMétricas básicas
softBounces, hardBounces, spamReports, unsubscribesInt
whatsappTemplateIdString?FK → WhatsappTemplate
whatsappNumberIdString?FK → StoreWhatsappNumber
attributionWindowDaysIntJanela de atribuição de receita (padrão: 7 dias)
behaviorRulesJson?Filtros de comportamento (include/exclude)
frequencyStringonce | daily | weekly | monthly
scheduleEndAtDateTime?Fim da recorrência
scheduleDayOfWeekInt?0–6 (semanal)
scheduleDayOfMonthInt?1–31 (mensal)

Índices: organizationId, storeId, [status, scheduledAt]


CampaignContent

Conteúdo de uma campanha (corpo do email ou mensagem).

CampoTipoDescrição
subjectString?Assunto do email
bodyStringCorpo (HTML para email, texto para WhatsApp)
mediaUrlString?URL de mídia (WhatsApp)
designJsonJson?Estado do editor visual

CampaignMetric

Métricas agregadas da campanha (1:1 com Campaign).

CampoTipoDescrição
sent, delivered, opens, clicksIntBásicas
softBounces, hardBounces, spamReports, unsubscribesInt
conversions, revenueInt/FloatAtribuição
reads, replies, failed, skippedIntWhatsApp-específicas
costFloatCusto estimado (soma das tarifas Meta)

StoreWhatsappNumber

Número WhatsApp de uma loja (Evolution ou Meta Cloud API).

CampoTipoDescrição
idUUIDPK
storeIdString?FK → Store (null para WABA do CRM)
providerStringmeta_cloud ou evolution
numberStringNúmero com DDD
wabaIdString?WhatsApp Business Account ID (Meta)
phoneNumberIdString?Phone Number ID (Meta Dashboard)
accessTokenString?Bearer token (criptografado AES-256-GCM)
appSecretString?Meta App Secret (criptografado)
webhookVerifyTokenString?hub.verify_token (armazenado como SHA-256)
messagingLimitTierString?TIER_50 | TIER_250 | TIER_1K | TIER_10K | TIER_100K | UNLIMITED
qualityScoreString?GREEN | YELLOW | RED
evolutionInstanceNameString?Nome da instância no Evolution API
evolutionApiKeyString?API key da instância (criptografada)
chatwootInboxIdString?ID do inbox no Chatwoot
isDefaultBooleanSe é o número padrão da org

WhatsappTemplate

Template de mensagem WhatsApp aprovado pela Meta.

CampoTipoDescrição
metaTemplateIdString?ID retornado pela Meta
nameStringsnake_case, único dentro do WABA
languageStringpt_BR
categoryStringMARKETING | UTILITY | AUTHENTICATION
statusStringPENDING | APPROVED | REJECTED | DISABLED | PAUSED | DRAFT
rejectionReasonString?Preenchido via webhook da Meta
componentsJsonArray de componentes Meta: HEADER, BODY, FOOTER, BUTTONS
variableMappingJson?{"1":"name","2":"phone"} — mapeamento CRM das variáveis
buttonConfigJson?[{smartRedirect, isUnsubscribe}] — flags por botão
hasDraftChangesBooleantrue quando há alterações locais não enviadas à Meta

WhatsappMessage

Mensagem individual enviada ou recebida via WhatsApp.

CampoTipoDescrição
wamIdString?ID único retornado pela Meta (único)
campaignIdString?FK → Campaign (null se inbox)
conversationIdString?FK → Conversation (null se campanha)
directionStringOUTBOUND | INBOUND
recipientPhoneStringNúmero do destinatário
statusStringSENT | DELIVERED | READ | FAILED
errorCode, errorMessageString?Se falhou
sentAt, deliveredAt, readAtDateTime?Timestamps dos status

Índices: wamId, campaignId, conversationId, organizationId, [recipientPhone, sentAt]


WhatsappPricingRate

Tarifas por conversa da Meta (importadas manualmente ou via API).

CampoTipoDescrição
phoneNumberIdStringFK para o número
countryStringISO alpha-2 (ex: BR)
categoryStringMARKETING | UTILITY | AUTHENTICATION | SERVICE
costPerConversationFloatCusto em USD
referenceMonthString2026-04

EmailSettings

Configuração SMTP por loja.

CampoTipoDescrição
storeIdStringFK → Store (único)
senderName, senderEmailStringRemetente
host, port, userString/IntSMTP
passStringSenha (criptografada AES-256-GCM)
secureBooleanTLS

OrganizationSmtpSettings

Configuração SMTP padrão da organização (fallback quando a loja não tem EmailSettings).

Mesmos campos que EmailSettings, mas vinculado à Organization.


Conversation

Conversa no Chatwoot/WhatsApp.

CampoTipoDescrição
storeId, customerIdStringQuem e onde
providerStringwhatsapp_cloud | evolution
statusStringOPEN | CLOSED | PENDING | RESOLVED
assignedToIdString?FK → User (vendedor responsável)
contactPhoneString?Número do contato
chatwootConversationIdString?ID no Chatwoot
resolvedAtDateTime?Quando foi resolvida
firstResponseSecondsInt?Tempo até primeira resposta
resolutionSecondsInt?Tempo total até resolução

Message

Mensagem individual dentro de uma Conversation.

CampoTipoDescrição
wamIdString?ID da Meta (único)
directionStringINBOUND | OUTBOUND
typeStringtext | template | image | document | audio
contentJsonEstrutura da Meta
statusStringSENT | DELIVERED | READ | FAILED

ChatwootConfig

Configuração da integração Chatwoot por organização.

CampoTipoDescrição
instanceUrlStringURL da instância Chatwoot
apiKeyStringChave de API (criptografada)
accountIdStringID da conta no Chatwoot
handoffMessageString?Mensagem ao transferir para atendente
storeGreetingMessageString?Saudação da loja
menuMessageString?Menu de seleção de loja
activeConversationMessageString?Mensagem se já há conversa aberta
connectingMessageString?Mensagem enquanto conecta
whatsappModeStringevolution | meta_cloud
waitTimeoutEnabledBooleanSe ativa timeout de espera
waitTimeoutMinutesIntTempo antes de reassign (padrão: 30)
surveyEnabledBooleanSe envia pesquisa ao fechar conversa

CampaignRedirectToken

Token de redirecionamento gerado para cada cliente em cada campanha.

CampoTipoDescrição
tokenStringUUID aleatório (único)
customerId, campaignIdStringFKs
preferredStoreIdString?Loja preferida para roteamento
clickedAtDateTime?Quando o cliente clicou
redirectedToString?URL final do redirect
expiresAtDateTimeValidade (padrão: 30 dias)

EmailTemplate

Template de email MJML reutilizável.

CampoTipoDescrição
nameStringNome do template
categoryStringCategoria
mjmlString (Text)Código MJML
designJsonJson?Estado do editor
thumbnailUrlString?Preview
usedCountIntQuantas vezes usado

EmailMessage

Envio individual de email (tracking por destinatário).

CampoTipoDescrição
campaignIdString?FK → Campaign
customerId, recipientEmailStringDestinatário
subjectStringAssunto
statusStringQUEUED | SENT | BOUNCED | FAILED
bounceTypeString?HARD | SOFT
trackingIdStringUUID para pixel de abertura/clique
openedAt, clickedAt, unsubscribedAt, sentAtDateTime?Eventos

PostSaleSurveyConfig

Configuração da pesquisa de satisfação pós-venda.

CampoTipoDescrição
typeStringCSAT | NPS
enabledBooleanSe ativa
delayHoursIntHoras após a venda para enviar
expirationDaysIntValidade do link
dedupeWindowDaysIntMínimo de dias entre pesquisas para o mesmo cliente
whatsappEnabled, whatsappNumberId, whatsappTemplateIdCanal WhatsApp
emailEnabled, emailSubjectCanal Email
questionsJson?Configuração das perguntas
primaryColor, bgColor1, bgColor2String?Visual personalizado

PostSaleSurveyJob

Job de envio de pesquisa para uma transação específica.

CampoTipoDescrição
transactionId, customerIdStringFKs
channelStringWHATSAPP | EMAIL
surveyTypeStringCSAT | NPS
statusStringPENDING | SENT | SKIPPED | FAILED
skipReasonString?Por que foi pulado
scheduledAt, sentAt, expiresAtDateTime?Ciclo de vida
surveyTokenHashString?SHA-256 do token (dedup)

Unique: [transactionId, channel, surveyType]


SurveyResponse / PostSaleSurveyResponse

Respostas de pesquisas de satisfação.

CampoTipoDescrição
conversationId (Survey) / jobId (PostSale)FK origem
ratingInt1–5 (CSAT) ou 0–10 (NPS)
commentString?Texto livre (max 500 chars)
tokenHashStringSHA-256 do token — dedup idempotente
submittedAtDateTimeQuando respondeu

Modelos auxiliares de integração Chatwoot/n8n

ModeloDescrição
SyncEventDeduplicação de webhooks Chatwoot por chatwootDeliveryId
SyncFailureWebhooks que falharam (ORPHAN_WAMID, CRM_UNREACHABLE, etc.)
WindowExpiryConversa Chatwoot encerrada por expiração da janela 24h
ConversationRedirectRegistro de redirects de campanha
CampaignNoMenuCliente que respondeu sem selecionar loja no menu
FailedWebhookWebhooks externos que falharam e precisam de retry

JobRun

Log de execução de jobs BullMQ.

CampoTipoDescrição
queueStringNome da fila
jobName, jobIdStringIdentificadores
statusStringcompleted | failed | active
durationMsInt?Duração em ms
errorString?Mensagem de erro
safePayloadJson?Payload sem dados sensíveis