> ## Documentation Index
> Fetch the complete documentation index at: https://lifters.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Session Replay — Triggers condicionais

> Gravar SÓ sessões críticas com URL/event trigger + ring buffer 60s pré-evento. Reduz storage 60-90%.

<Info>
  **Quando habilitar.** Sem trigger, o recorder começa em **t=0** e grava
  100% das sessões amostradas (`sampleRate`). Com trigger, o recorder **buffera**
  em memória até o gatilho disparar — só então o conteúdo dos últimos 60s
  começa a subir para R2. Resultado: 60-90% menos storage e custo.
</Info>

## Três modos

| Modo    | `trigger` config    | Quando dispara                                                   |
| ------- | ------------------- | ---------------------------------------------------------------- |
| `init`  | Ausente (default)   | t=0 — recorder sempre ativo desde o load                         |
| `url`   | `urlMatchRegex` set | Quando `location.pathname` casa o regex (boot OR SPA navigation) |
| `event` | `eventMatch` set    | Quando `iGamingSDK.capture(eventMatch, ...)` é chamado           |

Apenas **um** disparo por replay\_id. Após disparar, recorder fica em modo
streaming contínuo. Trigger seguintes são ignorados.

## Ring buffer (60s pré-trigger)

```
t=0 ─── t=30s ─── t=60s ─── t=90s ─── t=120s (TRIGGER) ─── stream
        [─────── ring buffer 60s ────────]
                                          ↑
                                          flush para sender + começa streaming
```

Eventos rrweb dos últimos 60s ficam num buffer circular. Eventos mais antigos
são automaticamente descartados (briefing v4 §5.6). Quando o trigger dispara:

1. Buffer é flushed → chunk enviado para R2 com `chunk_seq=0`
2. Recorder muda para modo streaming (eventos vão direto para sender)
3. Próximos chunks têm `chunk_seq=1, 2, ...`

Cutoff inteligente: se houver um full snapshot dentro da janela de 60s,
buffer corta tudo antes — porque eventos antes do full snapshot ficam
irreproduzíveis.

## Modo URL trigger

Caso clássico: gravar SÓ sessões que chegam em /deposito ou /saque.

```javascript theme={null}
window.iGamingSDK.init({
  // ... core ...
  sessionReplay: {
    enabled:    true,
    sampleRate: 1.0,  // amostrar 100% — o trigger filtra naturalmente
    trigger: {
      urlMatchRegex: '/(deposito|saque|cadastro|kyc)',
      bufferBeforeMs: 60000   // padrão; ajustável até 300000 (5min)
    }
  }
});
```

**Comportamento:**

* Jogador entra em `/cassino/slots` → recorder buffera mas nada sobe pro R2
* Jogador navega `/cassino/slots` → `/deposito` (SPA: `history.pushState`)
* SDK detecta route change (via `PageViewCollector.onRouteChange`)
* `triggers.checkUrl()` roda `urlMatchRegex.test('/deposito')` → ✅ true
* Buffer flush → chunk\_seq=0 contém os 60s anteriores (`/cassino/slots` + navegação)
* A partir daqui, streaming contínuo

**Regex válidos** (compilados via `new RegExp(...)` no SDK):

```javascript theme={null}
// Match exato:
urlMatchRegex: '^/deposito$'

// Múltiplas rotas:
urlMatchRegex: '/(deposito|saque|cadastro)'

// Sub-rotas:
urlMatchRegex: '/deposito(/.*)?'

// Excluir cards de marketing:
urlMatchRegex: '/cassino/(?!banner)'
```

<Warning>
  Regex inválido NÃO quebra o SDK — o trigger faz fallback para modo `init`
  (always-on) e o operador recebe console warn. Sempre teste regex no
  [regex101.com](https://regex101.com) (flavor JavaScript) antes de subir.
</Warning>

## Modo Event trigger

Caso clássico: gravar SÓ sessões com bet.placed acima de R\$ 5k.

```javascript theme={null}
window.iGamingSDK.init({
  // ... core ...
  sessionReplay: {
    enabled: true,
    sampleRate: 1.0,
    trigger: {
      eventMatch: 'bet.placed.high_value'
    }
  }
});

// Em outro ponto do app, quando o jogador confirma uma aposta:
function onBetConfirm(bet) {
  if (bet.amount_brl >= 5000) {
    window.iGamingSDK.capture('bet.placed.high_value', {
      bet_id: bet.id,
      amount: bet.amount_brl,
      game:   bet.game_slug
    });
  }
  // Para apostas <5k, evento normal é capturado mas NÃO dispara trigger.
  window.iGamingSDK.capture('bet.placed', { ... });
}
```

**Comportamento:**

* Operador chama `iGamingSDK.capture('bet.placed.high_value', {...})`
* Core SDK envia o evento Tipo 1 para `/v1/sdk-events` (canal normal)
* Core SDK ALSO emite via `sdkEventListeners` fanout para listeners externos
* `ReplayTriggerManager.handleSdkEvent('bet.placed.high_value')` → match → fire
* Buffer flush + streaming

Funciona com qualquer evento custom ou catálogo (`page.viewed`, `error.occurred`,
etc).

## Combinando URL + Event

Não há suporte nativo para múltiplos triggers em paralelo (briefing v4 §5.6
mantém intencionalmente simples). Workaround: usar `eventMatch` + capturar
o evento custom em qualquer URL que importe.

```javascript theme={null}
// Habilita trigger pelo evento "critical.path"
sessionReplay: {
  trigger: { eventMatch: 'critical.path' }
}

// No app, dispara o evento em múltiplos pontos críticos:
['enter_deposito', 'enter_saque', 'enter_kyc'].forEach((checkpoint) => {
  document.addEventListener(checkpoint, () => {
    iGamingSDK.capture('critical.path', { checkpoint });
  });
});
```

## Custo comparado

Operador 150k DAU, 12min sessão média:

| Estratégia                                                | Sessões gravadas/dia                        | Storage/mês | Custo R2/mês |
| --------------------------------------------------------- | ------------------------------------------- | ----------- | ------------ |
| Sem trigger (`sampleRate: 0.1`)                           | 15k                                         | 90 GB       | **\$1.40**   |
| Sem trigger (`sampleRate: 1.0`)                           | 150k                                        | 900 GB      | \$13.50      |
| `urlMatchRegex: /(deposito\|saque)` + `sampleRate: 1.0`   | \~12k (8% dos 150k vão para deposito/saque) | 75 GB       | **\$1.12**   |
| `eventMatch: 'bet.placed.high_value'` + `sampleRate: 1.0` | \~3k (high-rollers)                         | 18 GB       | **\$0.27**   |

<Tip>
  Em geral: prefer **trigger + sampleRate 1.0** sobre **sampleRate baixo sem
  trigger**. Trigger amostra POR CONTEXTO (página crítica = relevante),
  enquanto sampleRate amostra ALEATÓRIO — você pode perder o replay
  exatamente da sessão que precisava.
</Tip>

## Inspecionar trigger no Dashboard

```sql theme={null}
-- Quais triggers dispararam mais hoje?
SELECT trigger_source, count() AS replays
FROM atlas.tbt_sdk_replay_events
WHERE event_date = today()
GROUP BY trigger_source
ORDER BY replays DESC;
```

`trigger_source` é um dos 3 valores: `init`, `url`, `event`. Propagado pelo
SDK no envelope de cada chunk e replicado no row Gold.

## Próximos passos

<CardGroup cols={2}>
  <Card title="Masking & PII" icon="mask" href="/sdk/web-analytics/session-replay-masking">
    Sempre revise masking antes de subir sample rate.
  </Card>

  <Card title="Network & Console" icon="terminal" href="/sdk/web-analytics/session-replay-network-console">
    O que mais é capturado durante o replay.
  </Card>
</CardGroup>
