📊

Taxonomia de Eventos

Taxonomia de Eventos - Audissey

Versão: 1.0
Data: 20 de dezembro de 2025
Status: Para Validação pelo PO


📋 Índice

  1. Visão Geral
  2. Campos Comuns (Global Properties)
  3. Eventos de Sessão
  4. Eventos de Reprodução
  5. Eventos de Interação
  6. Eventos de Descoberta
  7. Eventos de Navegação
  8. Eventos de Utilizador
  9. Resumo por Objetivo
  10. Implementação Técnica

🎯 Visão Geral

Este documento define todos os eventos que serão medidos na aplicação Audissey para:

Princípios da Taxonomia

  1. Consistência: Nomes em snake_case, sempre em inglês
  2. Clareza: Nome do evento deve ser auto-explicativo
  3. Completude: Todos os campos necessários para análise
  4. Performance: Eventos não devem bloquear UX
  5. Privacy: Sem dados sensíveis nos eventos

🌐 Campos Comuns (Global Properties)

Estes campos devem ser enviados em todos os eventos:

Campo Tipo Obrigatório Descrição Exemplo
event_id string (UUID) ✅ Identificador único do evento "550e8400-e29b-41d4-a716-446655440000"
event_timestamp ISO 8601 ✅ Momento exato do evento (UTC) "2025-12-20T14:30:00.000Z"
user_id string (ObjectId) ✅ ID do utilizador (ou anonymous_id) "507f1f77bcf86cd799439011"
session_id string (UUID) ✅ ID da sessão atual "a3b2c1d0-1234-5678-9abc-def012345678"
device_type enum ✅ Tipo de dispositivo "mobile", "tablet", "desktop", "web"
device_os string ✅ Sistema operativo "iOS 17.2", "Android 14", "macOS 14.0"
app_version string ✅ Versão da app "1.2.3"
platform enum ✅ Plataforma "ios", "android", "web"
timezone string ✅ Timezone do utilizador "Europe/Lisbon"
hour_of_day integer (0-23) ✅ Hora local do utilizador 14
day_of_week enum ✅ Dia da semana local "monday", "tuesday", etc.
network_type enum ⚪ Tipo de conexão "wifi", "4g", "5g", "3g", "offline"
referrer_screen string ⚪ Ecrã anterior "home", "search", "favorites"

🔐 Eventos de Sessão

`session_started`

Objetivo: Produto + ML
Quando: Utilizador abre a app ou inicia nova sessão web

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
is_first_session boolean ✅ É a primeira sessão do utilizador true
days_since_last_session integer ⚪ Dias desde última sessão (null se primeira) 3
previous_session_duration_ms integer ⚪ Duração da sessão anterior (ms) 180000

Payload Exemplo:

{
  "event_name": "session_started",
  "event_id": "uuid-123",
  "event_timestamp": "2025-12-20T14:30:00.000Z",
  "user_id": "507f1f77bcf86cd799439011",
  "session_id": "session-uuid-456",
  "device_type": "mobile",
  "device_os": "iOS 17.2",
  "app_version": "1.2.3",
  "platform": "ios",
  "timezone": "Europe/Lisbon",
  "hour_of_day": 14,
  "day_of_week": "friday",
  "is_first_session": false,
  "days_since_last_session": 2
}

`session_ended`

Objetivo: Produto + ML
Quando: Utilizador fecha a app ou sessão expira (30min inatividade)

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
session_duration_ms integer ✅ Duração total da sessão (ms) 180000
content_played_count integer ✅ Número de conteúdos reproduzidos 5
screens_viewed_count integer ✅ Número de ecrãs visualizados 12
interactions_count integer ✅ Total de interações (plays, favorites, etc.) 8
end_reason enum ✅ Motivo do término "user_closed", "timeout", "crash"

▶️ Eventos de Reprodução

`play_started`

Objetivo: Produto + ML (CRÍTICO)
Quando: Utilizador inicia reprodução de rádio, podcast ou episódio

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f1f77bcf86cd799439011"
content_type enum ✅ Tipo de conteúdo "radio", "podcast_episode"
content_title string ✅ Título do conteúdo "Morning Show"
content_language string ⚪ Idioma do conteúdo "pt", "en"
content_genres array[string] ⚪ Géneros (radios) ou categorias (podcasts) ["news", "talk"]
radio_id string (ObjectId) ⚪ ID da rádio (se type=radio) "507f..."
podcast_id string (ObjectId) ⚪ ID do podcast (se type=podcast_episode) "507f..."
program_id string (ObjectId) ⚪ ID do programa (se disponível) "507f..."
duration_seconds integer ⚪ Duração conhecida (null para rádio) 1800
source enum ✅ Origem da reprodução "recommendation", "search", "favorites", "category", "trending", "continue_listening", "radio_program"
position_in_list integer ⚪ Posição na lista (se aplicável) 3
playback_quality enum ⚪ Qualidade de áudio "low", "medium", "high"

Payload Exemplo:

{
  "event_name": "play_started",
  "event_id": "uuid-789",
  "event_timestamp": "2025-12-20T14:35:22.000Z",
  "user_id": "507f1f77bcf86cd799439011",
  "session_id": "session-uuid-456",
  "device_type": "mobile",
  "hour_of_day": 14,
  "day_of_week": "friday",
  "content_id": "507f1f77bcf86cd799439012",
  "content_type": "podcast_episode",
  "content_title": "Como criar startups de sucesso",
  "content_language": "pt",
  "content_genres": ["business", "entrepreneurship"],
  "podcast_id": "507f1f77bcf86cd799439013",
  "duration_seconds": 2400,
  "source": "recommendation",
  "position_in_list": 2
}

`play_progress`

Objetivo: ML (CRÍTICO)
Quando: A cada 30 segundos durante reprodução

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo de conteúdo "radio", "podcast_episode"
playback_position_seconds integer ✅ Posição atual (segundos) 120
duration_seconds integer ⚪ Duração total (null para rádio) 2400
percent_completed float ⚪ Percentagem ouvida (null para rádio) 0.05
buffer_count integer ✅ Número de bufferings 1
is_background boolean ✅ Reprodução em background false

`play_paused`

Objetivo: Produto
Quando: Utilizador pausa reprodução

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo de conteúdo "radio", "podcast_episode"
playback_position_seconds integer ✅ Posição ao pausar 150
duration_seconds integer ⚪ Duração total 2400
pause_reason enum ⚪ Motivo da pausa "user_action", "call_incoming", "notification", "error"

`play_resumed`

Objetivo: Produto
Quando: Utilizador retoma reprodução após pausa

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo de conteúdo "radio", "podcast_episode"
playback_position_seconds integer ✅ Posição ao retomar 150
pause_duration_seconds integer ✅ Tempo pausado 30

`play_ended`

Objetivo: Produto + ML (CRÍTICO)
Quando: Reprodução termina (completada ou interrompida pelo utilizador)

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo de conteúdo "radio", "podcast_episode"
playback_duration_seconds integer ✅ Tempo total reproduzido 180
content_duration_seconds integer ⚪ Duração total do conteúdo 2400
percent_completed float ⚪ Percentagem ouvida 0.075
completion_threshold_reached boolean ✅ Ouviu >80% (para podcasts) false
end_reason enum ✅ Motivo do término "completed", "user_stopped", "user_skipped", "error", "next_content"
buffer_count integer ✅ Total de bufferings 2
average_bitrate_kbps integer ⚪ Bitrate médio 128

`play_seek`

Objetivo: Produto
Quando: Utilizador salta para posição diferente (apenas podcasts)

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo (sempre podcast_episode) "podcast_episode"
from_position_seconds integer ✅ Posição anterior 120
to_position_seconds integer ✅ Nova posição 300
seek_direction enum ✅ Direção "forward", "backward"

💖 Eventos de Interação

`favorite_added`

Objetivo: Produto + ML (CRÍTICO)
Quando: Utilizador adiciona aos favoritos

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo "radio", "podcast"
content_title string ✅ Título "RFM"
source enum ✅ De onde adicionou "player", "detail_page", "list"
was_playing boolean ✅ Estava a reproduzir true

`favorite_removed`

Objetivo: Produto + ML
Quando: Utilizador remove dos favoritos

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo "radio", "podcast"
source enum ✅ De onde removeu "player", "favorites_list"
days_as_favorite integer ✅ Dias como favorito 15

`content_shared`

Objetivo: Produto
Quando: Utilizador partilha conteúdo

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo "radio", "podcast", "podcast_episode"
share_method enum ✅ Método "whatsapp", "twitter", "facebook", "instagram", "copy_link", "email"
source enum ✅ De onde partilhou "player", "detail_page"

`search_performed`

Objetivo: Produto + ML
Quando: Utilizador faz pesquisa

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
search_query string ✅ Texto pesquisado "podcasts tecnologia"
search_filters object ⚪ Filtros aplicados {"type": "podcast", "language": "pt"}
results_count integer ✅ Número de resultados 23
search_duration_ms integer ✅ Tempo de resposta 150

`search_result_clicked`

Objetivo: Produto + ML (CRÍTICO)
Quando: Utilizador clica num resultado de pesquisa

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
search_query string ✅ Texto pesquisado "podcasts tecnologia"
content_id string (ObjectId) ✅ ID do conteúdo clicado "507f..."
content_type enum ✅ Tipo "radio", "podcast", "podcast_episode"
position_in_results integer ✅ Posição na lista (1-indexed) 3
total_results integer ✅ Total de resultados 23

`rating_submitted`

Objetivo: Produto + ML
Quando: Utilizador avalia conteúdo (se implementado)

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo "radio", "podcast", "podcast_episode"
rating_value integer (1-5) ✅ Avaliação 4
previous_rating integer ⚪ Avaliação anterior (null se primeira) 3

🔍 Eventos de Descoberta

`content_impression`

Objetivo: ML (CRÍTICO)
Quando: Conteúdo é exibido no ecrã (viewport) por >1 segundo

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo "radio", "podcast", "podcast_episode"
content_title string ✅ Título "Tech News Daily"
list_type enum ✅ Tipo de lista "recommendations", "trending", "search", "category", "favorites", "continue_listening"
position_in_list integer ✅ Posição (1-indexed) 5
impression_duration_ms integer ✅ Tempo visível 2500

Nota: Crítico para negative implicit feedback (viu mas não clicou)


`content_viewed`

Objetivo: Produto
Quando: Utilizador abre página de detalhes do conteúdo

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
content_id string (ObjectId) ✅ ID do conteúdo "507f..."
content_type enum ✅ Tipo "radio", "podcast"
source enum ✅ Origem "recommendation", "search", "favorites", "category"
view_duration_ms integer ⚪ Tempo na página (enviado ao sair) 5000

`list_scrolled`

Objetivo: Produto
Quando: Utilizador faz scroll numa lista

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
list_type enum ✅ Tipo de lista "recommendations", "trending", "search", "category"
scroll_depth float (0-1) ✅ Profundidade do scroll 0.75
items_viewed integer ✅ Itens visualizados 12
total_items integer ✅ Total de itens 50

🧭 Eventos de Navegação

`screen_viewed`

Objetivo: Produto
Quando: Utilizador acede a um ecrã/página

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
screen_name enum ✅ Nome do ecrã "home", "search", "favorites", "player", "profile", "radio_detail", "podcast_detail", "episode_detail", "category"
screen_category enum ⚪ Categoria do ecrã "discovery", "consumption", "profile", "settings"
previous_screen string ⚪ Ecrã anterior "home"

`tab_changed`

Objetivo: Produto
Quando: Utilizador muda de tab na navegação principal

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
from_tab enum ✅ Tab anterior "home", "radios", "podcasts", "favorites", "profile"
to_tab enum ✅ Nova tab "radios"

👤 Eventos de Utilizador

`user_registered`

Objetivo: Produto
Quando: Utilizador completa registo

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
registration_method enum ✅ Método de registo "email", "google", "apple", "facebook"
has_profile_name boolean ✅ Preencheu nome true
has_preferred_language boolean ✅ Definiu idioma false

`user_logged_in`

Objetivo: Produto
Quando: Utilizador faz login

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
login_method enum ✅ Método de login "email", "google", "apple", "facebook"
days_since_last_login integer ⚪ Dias desde último login 5

`profile_updated`

Objetivo: Produto
Quando: Utilizador atualiza perfil

Campos Específicos:

Campo Tipo Obrigatório Descrição Exemplo
fields_updated array[string] ✅ Campos alterados ["name", "preferredLanguage"]

`user_logged_out`

Objetivo: Produto
Quando: Utilizador faz logout

Campos Específicos: Apenas campos comuns


📊 Resumo por Objetivo

Eventos CRÍTICOS para ML (Modelo de Recomendação)

  1. ✅ play_started - Implicit positive feedback
  2. ✅ play_progress - Engagement depth
  3. ✅ play_ended - Completion rate
  4. ✅ favorite_added - Explicit positive feedback
  5. ✅ content_impression - Negative implicit feedback (viu mas não interagiu)
  6. ✅ search_result_clicked - Intent signal

Eventos para Produto (Métricas & Analytics)

Eventos para Ambos (Produto + ML)


🔧 Implementação Técnica

Priorização de Eventos (MVP)

Fase 1 - Essenciais (Semana 1-2)

✅ session_started
✅ session_ended
✅ play_started
✅ play_progress (30s)
✅ play_ended
✅ favorite_added
✅ favorite_removed

Fase 2 - Importantes (Semana 3-4)

✅ content_impression
✅ search_performed
✅ search_result_clicked
✅ screen_viewed
✅ user_registered
✅ user_logged_in

Fase 3 - Nice-to-Have (Após MVP)

⚪ play_paused
⚪ play_resumed
⚪ play_seek
⚪ content_shared
⚪ rating_submitted
⚪ list_scrolled

Stack de Analytics Sugerida

Opção 1: Open Source

Opção 2: Managed

Opção 3: Hybrid (Recomendado)

Estrutura de Envio

// SDK de Analytics (exemplo)
interface AnalyticsEvent {
  event_name: string;
  event_id: string;
  event_timestamp: string;
  user_id: string;
  session_id: string;
  // ... campos comuns
  properties: Record<string, any>; // Campos específicos do evento
}

// Exemplo de uso
analytics.track('play_started', {
  content_id: '507f1f77bcf86cd799439011',
  content_type: 'podcast_episode',
  content_title: 'Como criar startups',
  source: 'recommendation',
  position_in_list: 2
});

Validação de Eventos

// Schema validation com Zod
import { z } from 'zod';

const PlayStartedSchema = z.object({
  event_name: z.literal('play_started'),
  content_id: z.string().regex(/^[a-f\d]{24}$/i),
  content_type: z.enum(['radio', 'podcast_episode']),
  content_title: z.string().min(1),
  source: z.enum([
    'recommendation',
    'search',
    'favorites',
    'category',
    'trending',
    'continue_listening',
    'radio_program'
  ]),
  position_in_list: z.number().int().positive().optional(),
  // ... outros campos
});

Sampling Strategy

Para apps com alto volume (>1M eventos/dia):

Performance Guidelines

  1. Eventos não devem bloquear UI

    • Envio assíncrono
    • Queue local com retry
    • Batch de eventos (máx 50 por request)
  2. Payloads leves

    • Máximo 10KB por evento
    • Evitar arrays grandes
    • Comprimir se >5KB
  3. Rate limiting

    • Máximo 10 eventos/segundo por utilizador
    • Deduplicação por event_id

Privacy & GDPR

  1. Dados Sensíveis - NUNCA enviar:

    • Passwords
    • Emails completos (usar hash)
    • IP addresses completos
    • Números de telefone
  2. Consent:

    • Analytics essenciais: Não requer consent
    • ML/Personalização: Requer opt-in
    • Marketing: Requer opt-in explícito
  3. Data Retention:

    • Eventos de produto: 2 anos
    • Eventos de ML: 1 ano
    • Agregados: Indefinido
  4. Right to be Forgotten:

    • Anonimizar user_id após 30 dias do pedido
    • Manter agregados sem PII

✅ Checklist de Validação

Para o PO/Founder validar:

Perguntas para Discussão:

  1. Rádios em direto: Como medir engagement se não há "duration"? (Resposta atual: play_progress a cada 30s)
  2. Programs: Queremos trackear qual programa estava a ser emitido quando user ouviu? (Resposta: Sim, via program_id em play_started)
  3. Offline playback: Como trackear? (Sugestão: Queue de eventos, enviar quando voltar online)
  4. Notifications: Trackear push notifications? (Sugestão: Fase 3)
  5. Sleep timer: Implementar evento sleep_timer_set? (Sugestão: Fase 3)

📝 Changelog

Data Versão Alteração
2025-12-20 1.0 Versão inicial para validação

👥 Aprovações


Próximos Passos:

  1. ✅ Validação deste documento pelo PO
  2. ⏳ Implementação SDK de analytics (Fase 1)
  3. ⏳ Setup de infrastructure (BigQuery/Mixpanel)
  4. ⏳ Integração nos clients (iOS/Android/Web)
  5. ⏳ Dashboard de validação (verificar eventos a chegar)