Menuabrir
Em fluxoAtualizado em 14 de mai. de 2026, 00:06

Este módulo depende de

2
  • attributionappend_touchpoint(channel='website', source_module='links') em click
  • crmLink type scoring aplicado em CRM scoring rules

Módulos que dependem deste

0

Nenhum módulo depende deste hoje.

Links (Smart Link Shortener Premium)

Status: 🟡 Em fluxo (premium tier maturo, custom domains ativo) Code: backend/app/modules/links UI: frontend/src/app/(admin)/[slug]/admin/links Última revisão deste doc: 2026-05-13 por Felipe + Claude Dependências fortes: crm (scoring por link_type), attribution (touchpoints), tracking (clicks)


1. Identidade

O que faz (uma frase)

Encurtador de URLs premium com routing rules condicional, email gates, smart links com password, custom domains, A/B variants por canal, version history e geolocation tracking — gera URLs go.mandir.com.br/<slug> ou go.{custom}.com.br/<slug> que registram cada click.

Por que existe (negócio)

Encurtadores comerciais (bit.ly, rebrandly) cobram US$10-30/mês por features básicas e não cruzam com CRM/intelligence. Sem este módulo:

  • Bit.ly stats fica isolado.
  • Sem segmentação por canal (Instagram bio vs WhatsApp story).
  • Sem captura de email no clique.

Com Links integrado:

  • CRM scoring: link tipo "VIP" vale 50 pontos; "newsletter" vale 5.
  • A/B per canal: link Instagram → variante A; link Twitter → variante B.
  • Email gate: visitante precisa email pra ver conteúdo (lead magnet inteligente).
  • Custom domain: go.maisconsciente.com.br/aula (em vez de bitly).
  • Touchpoint attribution: click registra contact_touchpoint automaticamente.

Status atual

  • Premium tier maturo. Email gates, smart routing, custom domains todos ativos.

Próxima mudança: integração com mais providers GeoIP; analytics dashboard cross-link comparativo.


2. Cases de uso reais

Felipe cria link "PDF Mantras" → email_gate=true. Visitante clica → form pede email → submete → cria links_email_capture + links_email_session(expires_at=now+24h) → libera target_url. Próximo click do mesmo visitor_id antes de 24h pula form.

Smart link "App Download": condição device_kind=android → google_play_url; device_kind=ios → app_store_url; default → landing genérica. Sem JS no client — backend decide.

Case 3: Custom domain

Felipe cadastra go.maisconsciente.com.br → backend gera verification_token → Felipe coloca CNAME no DNS. Backend verifica → verified_at=now. Links criados com domain="go.maisconsciente.com.br" resolvem nessa URL.


3. Oportunidades de negócio

  • Link aggregator standalone: "Mandir Bio" tipo Linktree premium com automação CRM.
  • A/B link testing: estatística automática "qual variante teve mais conversão".
  • Email lead magnet workflow: form gate + sequência email automática + score CRM = micro-funnel completo.
  • Custom domain marketplace: clientes vendem subdomínios (go.<seu-brand>.com.br).

4. Arquitetura interna

Arquivos

  • models.py — 8 tabelas.
  • routes.py — 27 endpoints + 1 público redirect.
  • service.py — slug generation, click tracking, geolocation, routing rules parser, email gate.

Tasks

Sem Celery. Click tracking síncrono.

Adapter

  • MaxMind GeoIP opcional — env MAXMIND_ACCOUNT_ID + MAXMIND_LICENSE_KEY. Graceful fallback NULL country/city.
  • argon2 pra password hashing.

5. Tabelas (8)

Categorias (code, name, points_default, color, icon, sort_order, is_active). Ex: VIP=50pts, newsletter=5pts.

Shortlink principal.

Coluna chaveNotas
slugUNIQUE per tenant (6-64 chars; auto-gerado fallback)
target_urlDestino
title / tags (JSONB)
utm_* (5 cols)Auto-append na target_url
tracking_eventsJSONB smart rules
routing_rulesJSONB DSL (device, country, dayofweek)
password_hashOptional argon2
email_gate (BOOL)Lead magnet
intersticial_seconds / intersticial_message
allowed_emails / max_sessions_per_email / email_session_ttl_hours (24 default)
email_session_window_start / _endSoft constraints
domainCustom domain ou NULL = go.mandir.com.br
link_type_idFK
custom_pointsOverride scoring
crm_filtersJSONB
click_cap / click_cap_per_visitor / click_cap_per_visitor_window_hours (24 default)Rate limits
is_active / expires_at
click_count / last_click_atCache

Variant per canal (link_id, code=8 chars unique global, label, channel, ref_name, click_count, last_click_at).

Evento click.

ColunaNotas
link_idFK
visitor_id (UUID)Cookie-based
ip (INET) / user_agent / referrer
country / cityMaxMind
device_kindmobile_ios / mobile_android / desktop / etc.
extraJSONB
created_at (idx)

Email gate harvest.

ColunaNotas
link_id / email / name / visitor_id / ip / user_agent

Sessão ativa email gate.

ColunaNotas
link_id / email / visitor_id / ip / user_agent
created_at / expires_atTTL 24h default
released_at / released_reasonQuando admin libera manualmente
ColunaNotas
domainUNIQUE GLOBAL (não por tenant!)
verification_token / verified_atCNAME flow
is_active

Histórico snapshots pré-PATCH (link_id, version_num, snapshot JSONB, created_at).

Relacionamentos cross-módulo

DireçãoOutro móduloComo
↘ Escreveattributionappend_touchpoint(channel='website', source_module='links') em click
↘ Emite eventtrackinglinks.click
↗ Lêcrm_scoring_rulePoints por link_type aplicado em CRM

6. API / Endpoints (~27)

Prefixo /api/links.

MétodoRotaO que faz
GET / POST / PATCH / DELETE/typesCRUD
MétodoRotaO que faz
GET / POST / GET{id} / PATCH / DELETE/linksCRUD

Clicks / Health / Versions

MétodoRotaO que faz
GET/links/{id}/clicksLista filtered
GET/links/{id}/healthStatus
GET/links/{id}/versionsHistórico
POST/links/{id}/versions/{n}/restoreRestore como new version

Email gates

MétodoRotaO que faz
GET/links/{id}/capturesList CSV export
GET/links/{id}/sessionsList ativas
POST/links/{id}/sessions/{id}/releaseManual release

Shares

MétodoRotaO que faz
GET / POST / DELETE/links/{id}/sharesCRUD variants

Domains (3)

MétodoRotaO que faz
GET / POST/domainsList / Create
POST/domains/{id}/verifyCheck CNAME
DELETE/domains/{id}Delete

Simulate (1)

MétodoRotaO que faz
POST/links/{id}/simulateDry-run routing rules

Public redirect (1)

MétodoRotaAuthO que faz
GET/{slug} (no /api)Público302 redirect (registra click)

7. Configuração

Env vars

VarPropósito
MAXMIND_ACCOUNT_IDGeoIP optional
MAXMIND_LICENSE_KEYGeoIP optional
LINKS_DEFAULT_DOMAINgo.mandir.com.br (hardcoded fallback)

Custom domain DNS setup

Cliente coloca CNAME go.{custom}.com.br → *.go.mandir.com.br. Backend verifica via verification_token em DNS TXT.

Kill switches

  • links_link.is_active=false → 410 Gone.
  • links_link.expires_at<now → 410 Gone.
  • click_cap excedido → 429 Too Many Requests.

8. Operações

  1. UI /admin/links → "Novo" → form (slug, target_url, email_gate=true, allowed_emails opcional).
  2. Compartilhar URL https://go.mandir.com.br/<slug>.
  3. Visitante click → form email → submete → libera.

Troubleshooting

Sintoma: Slug duplicado em conflict

Causa: Auto-gerado bateu com existente (raro 6 chars random). Fix: Tenta novamente (até 5x) ou user passa slug manual.


9. Limitações e débitos técnicos

#Item
1Click cap não atomic — race condition (acceptable pra lead gates)
2email_session window soft — não bloqueia, só metadata
3Share code uniqueness GLOBAL — não per tenant (design choice)
4Sem time-travel rollback — version restore = new version
5MaxMind opcional — sem ele, country/city = NULL

10. Histórico

Premium tier construído iterativamente:

  • Sprint 1 — basic shortener.
  • Sprint 2 — email gates + smart links.
  • Sprint 3 — custom domains.
  • Sprint 4 — share codes + attribution.
  • Sprint 5 — tracking integration.