Analytics API Routes
Rotas de analytics e métricas. Todas exigem autenticação e são scoped por account_id.
GET /api/analytics
Seção intitulada “GET /api/analytics”Descrição: Retorna um dashboard completo com métricas agregadas: contatos (total, novos esta semana/mes), mensagens (enviadas, recebidas, taxa de entrega/leitura), conversas (total, ativas), campanhas (total, este mês), automações ativas.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request: N/A
Response:
- 200:
{ "data": { "contacts": { "total": 500, "new_this_week": 12, "new_this_month": 45 }, "messages": { "total_sent": 1200, "total_received": 800, "delivery_rate": 98.5, "read_rate": 65.2 }, "conversations": { "total": 300, "active": 42 }, "campaigns": { "total": 15, "this_month": 3 }, "automations": { "active": 8 } }}Notas: Usa Promise.all com 8 queries paralelas. Message stats via RPC message_stats. Delivery rate = delivered / sent * 100. Read rate = read / delivered * 100. Conversas ativas = status != “closed”.
GET /api/analytics/messages
Seção intitulada “GET /api/analytics/messages”Descrição: Retorna estatísticas de mensagens por dia (sent, delivered, read, failed, received) e totais agregados.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Query params:
period(string, default: “7d”) - “7d” | “30d” | “90d”
Response:
- 200:
{ "data": { "period": "7d", "daily": [ { "date": "2026-02-13", "sent": 50, "delivered": 48, "read": 30, "failed": 2, "received": 25 } ], "totals": { "sent": 350, "delivered": 336, "read": 210, "failed": 14, "received": 175 } }}Notas: Usa Zod analyticsQuerySchema para validação do period. RPC message_stats_by_day com parametros p_account_id e p_days_back.
GET /api/analytics/campaigns
Seção intitulada “GET /api/analytics/campaigns”Descrição: Retorna analytics de campanhas executadas: nome, template, recipients, delivered, read, failed, e taxas calculadas.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Query params:
period(string, default: “30d”) - “30d” | “90d”
Response:
- 200:
{ "data": { "campaigns": [ { "id": "uuid", "name": "Black Friday", "template_name": "promo_bf_2026", "executed_at": "2026-02-10T...", "total_recipients": 500, "delivered": 490, "read": 300, "failed": 10, "delivery_rate": 98.0, "read_rate": 61.2 } ] }}Notas: Limita a 20 campanhas mais recentes. Usa RPC campaign_stats_agg para cada campanha (N+1 query, max 20).
GET /api/analytics/export
Seção intitulada “GET /api/analytics/export”Descrição: Exporta dados em formato CSV. Suporta exportação de mensagens, contatos e campanhas. Retorna arquivo CSV com BOM UTF-8.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Query params:
type(string, obrigatório) - “messages” | “contacts” | “campaigns”period(string, default: “30d”) - “7d” | “30d” | “90d” (usado apenas para messages)
Response:
- 200:
text/csvfile download- Messages: colunas
Data, Direção, Tipo, Status, Conteúdo - Contacts: colunas
Nome, Telefone, Email, Criado em - Campaigns: colunas
Nome, Status, Criado em, Agendado para
- Messages: colunas
- 422: Tipo inválido
Notas:
- Limite de 10.000 registros por exportação
- CSV injection protection: valores que começam com
=,+,-,@, tab, newline são prefixados com' - NAO usa
withErrorHandlingwrapper — error handling manual com try/catch - Header
Content-Dispositioncom filename{type}_export.csv
GET /api/analytics/heatmap
Seção intitulada “GET /api/analytics/heatmap”Descrição: Retorna dados de heatmap de atividade de mensagens inbound por dia da semana e hora (UTC).
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Query params:
period(string, default: “7d”) - “7d” | “30d” | “90d”
Response:
- 200:
{ "data": [ { "day": 0, "hour": 14, "count": 25 }, { "day": 1, "hour": 9, "count": 42 } ]}day: 0=domingo … 6=sabado (UTC)hour: 0-23 (UTC)
Notas: Busca até 1000 conversas e 10.000 mensagens inbound. Processamento em memória (Map).
GET /api/analytics/rfm
Seção intitulada “GET /api/analytics/rfm”Descrição: Retorna a distribuição RFM (Recency, Frequency, Monetary) dos contatos. Só está habilitado se o account tiver pedidos pagos suficientes.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request: N/A
Response:
- 200:
{ "data": { "enabled": true, "orderCount": 150, "distribution": [ { "segment": "Champions", "count": 30, "last_calculated_at": "2026-02-20T..." } ], "lastCalculatedAt": "2026-02-20T..." }}- Se desabilitado:
{ "enabled": false, "orderCount": 5, "distribution": [], "lastCalculatedAt": null }
Notas: Mínimo de pedidos definido por MIN_ORDERS_FOR_RFM (constante em src/lib/rfm/constants). Usa RPC rfm_distribution.
POST /api/analytics/rfm/recalculate
Seção intitulada “POST /api/analytics/rfm/recalculate”Descrição: Dispara recalculo assíncrono dos scores RFM do account. Fire-and-forget (retorna imediatamente).
Auth: Required (getAuthContext()) + role “owner” ou “admin”
Rate Limit: Nenhum
Audit: Nenhum
Request: N/A
Response:
- 200:
{ "data": { "message": "Recalculo iniciado" } } - 403:
{ "data": { "error": "Apenas owner ou admin podem recalcular" } }(nota: retorna 200 com erro no body)
Notas: A função recalculateRfmScores() roda em background com .catch(). Erros são logados mas não retornados ao client. Validação de role feita manualmente (não usa requireRole).
GET /api/analytics/revenue
Seção intitulada “GET /api/analytics/revenue”Descrição: Retorna dashboard completo de revenue com overview, serie temporal diaria, revenue por automação, por campanha, e por link de tracking.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Query params:
period(string, default: “30d”) - “7d” | “30d” | “90d”
Response:
- 200:
{ "data": { "overview": { "totalRevenue": 50000.00, "attributedRevenue": 35000.00, "productCosts": 15000.00, "gatewayFees": 1500.00, "adSpend": 3000.00, "profit": 15500.00, "roas": 11.67, "totalOrders": 200, "attributedOrders": 140, "avgOrderValue": 250.00 }, "daily": [ { "date": "2026-02-13", "totalRevenue": 1500.00, "attributedRevenue": 1000.00, "orderCount": 6 } ], "byAutomation": [ { "automationId": "uuid", "automationName": "Carrinho", "revenue": 5000.00, "orderCount": 20, "avgOrderValue": 250.00 } ], "byCampaign": [ { "campaignId": "uuid", "campaignName": "Promo BF", "revenue": 10000.00, "orderCount": 40 } ], "byLink": [ { "linkId": "uuid", "shortCode": "abc123", "originalUrl": "https://...", "clicks": 500, "orders": 25, "revenue": 6250.00, "conversionRate": 5.0 } ] }}Notas: Usa 5 RPCs paralelas: revenue_with_costs, revenue_by_day, revenue_by_automation, revenue_by_campaign, revenue_by_link. byLink limitado a 20 resultados.
GET /api/analytics/ecommerce
Seção intitulada “GET /api/analytics/ecommerce”Descrição: Retorna dashboard de ecommerce: overview (pedidos, revenue, ticket medio, carrinhos, recovery rate), dados por plataforma, top produtos, e pedidos por dia.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Query params:
period(string, default: “30d”) - “7d” | “30d” | “90d”
Response:
- 200:
{ "data": { "overview": { "total_orders": 200, "total_revenue": 50000, "avg_ticket": 250, "pending_orders": 5, "cancelled_orders": 3, "refunded_orders": 1, "total_carts": 80, "recovered_carts": 15, "recovery_rate": 18.75 }, "byPlatform": ["..."], "topProducts": ["..."], "daily": ["..."] }}Notas: Usa 4 RPCs paralelas: ecommerce_overview, ecommerce_by_platform, ecommerce_top_products (limit 10), ecommerce_orders_by_day.
GET /api/analytics/automations
Seção intitulada “GET /api/analytics/automations”Descrição: Retorna métricas de execução de automações: total, completed, failed, latencia media por automação, e summary geral.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Query params:
period(string, default: “7d”) - “7d” | “30d” | “90d”
Response:
- 200:
{ "data": { "data": [ { "automation_id": "uuid", "automation_name": "Boas-vindas", "total_executions": 150, "completed": 145, "failed": 5, "avg_latency_seconds": 2.3 } ], "summary": { "totalExecutions": 500, "successRate": 97.0, "avgLatency": 1.8 } }}Notas: Usa RPC get_automation_execution_stats. Resultado limitado a top 20 automações por total_executions. Nomes das automações buscados separadamente da tabela automations.
GET /api/analytics/metrics
Seção intitulada “GET /api/analytics/metrics”Descrição: Lista todas as métricas customizadas (events) do account, agrupadas por nome, com contagem de eventos, valor total, fontes, e última ocorrencia.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Query params:
source(string, opcional) - filtro por source do evento
Response:
- 200:
{ "data": { "metrics": [ { "metric_name": "Order Placed", "display_name": "Pedido Realizado", "sources": ["shopify", "yampi"], "total_events": 500, "total_value": 125000.00, "last_event_at": "2026-02-20T...", "category": "ecommerce" } ], "available_sources": ["shopify", "yampi", "manual"] }}Notas: Agrupamento e calculo feitos em memória (Maps). Display names vem da tabela metrics ou do helper getMetricDisplayName(). Sorted por total_events desc.
GET /api/analytics/metrics/[name]
Seção intitulada “GET /api/analytics/metrics/[name]”Descrição: Retorna detalhes de uma métrica específica: resumo (total eventos, valor, media, contatos únicos) e serie temporal diaria.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Path params:
name(string, URL-encoded) - nome da métrica - Query params:
period(string, default: “30d”) - “7d” | “30d” | “90d”
Response:
- 200:
{ "data": { "metric_name": "Order Placed", "display_name": "Pedido Realizado", "period": "30d", "summary": { "total_events": 150, "total_value": 37500.00, "avg_value": 250.00, "unique_contacts": 120 }, "daily": [ { "date": "2026-02-13", "count": 5, "value": 1250.00 } ] }}Notas: Serie diaria preenche dias sem eventos com count: 0, value: 0. Path param e decodificado com decodeURIComponent().
GET /api/analytics/metrics/[name]/top-contacts
Seção intitulada “GET /api/analytics/metrics/[name]/top-contacts”Descrição: Retorna os contatos com maior valor total para uma métrica específica.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Path params:
name(string, URL-encoded) - nome da métrica - Query params:
limit(number, default: 10, min: 1, max: 50) - quantidade de contatos
Response:
- 200:
{ "data": { "contacts": [ { "contact_id": "uuid", "contact_name": "Joao Silva", "event_count": 15, "total_value": 3750.00, "last_event_at": "2026-02-20T..." } ] }}Notas: Usa tabela materializada contact_metrics_summary ordenada por total_value desc. Nomes dos contatos buscados em batch.
GET /api/analytics/metrics/[name]/activity
Seção intitulada “GET /api/analytics/metrics/[name]/activity”Descrição: Retorna feed de atividade (eventos individuais) de uma métrica com paginação por cursor.
Auth: Required (getAuthContext())
Rate Limit: Nenhum
Audit: Nenhum
Request:
- Path params:
name(string, URL-encoded) - nome da métrica - Query params:
limit(number, default: 20, min: 1, max: 100) - itens por páginacursor(string, uuid, opcional) - ID do último evento da página anterior
Response:
- 200:
{ "data": { "items": [ { "id": "uuid", "contact_name": "Maria Souza", "contact_id": "uuid", "value": 250.00, "currency": "BRL", "source": "shopify", "properties": { "order_id": "12345" }, "occurred_at": "2026-02-20T..." } ], "nextCursor": "uuid-do-ultimo-item | null" }}Notas: Páginação cursor-based usando occurred_at + id como composite cursor. Busca limit + 1 para determinar hasMore. Nomes dos contatos buscados em batch por IDs únicos.