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

# Event store e auditoria

> Trilha de auditoria imutável e reconstrução de decisões.

Toda mudança de estado relevante na Repass (criar um Afiliado, registrar uma Conversão, aprovar uma Comissão, pagar um Payout) grava o novo estado **e** um evento de domínio. O conjunto desses eventos forma um log único, imutável e ordenado: o **event store**.

Diferente da maioria das plataformas, esse log é exposto como recurso de API (`GET /events`). Ele é a sua trilha de auditoria completa: você consegue responder "o que aconteceu, quando, e quem causou" para qualquer recurso, e reconstruir a sequência de decisões que levou a uma Comissão ou Payout específico. O mesmo log é o que alimenta os [Webhooks](/docs/webhooks/visao-geral).

## O que é um evento

Um evento de domínio é o registro imutável de um fato de negócio. Nasce e nunca muda. Cada evento tem um envelope padronizado, idêntico no `GET /events/{id}` e no body entregue aos Webhooks:

<ResponseField name="id" type="string">
  Identificador `evt_<ULID>`. O ULID dá ordenação cronológica natural: é o cursor de paginação e a ordem em que os eventos são entregues nos Webhooks.
</ResponseField>

<ResponseField name="type" type="string">
  Tipo namespaced `recurso.ação` (ex.: `commission.paid`, `conversion.created`). Veja o [catálogo de eventos](/docs/webhooks/catalogo-de-eventos).
</ResponseField>

<ResponseField name="version" type="integer">
  Versão do **schema do payload** daquele tipo. Hoje todos os tipos estão em `version: 1`.
</ResponseField>

<ResponseField name="aggregateType" type="string">
  Tipo do agregado a que o evento se refere (ex.: `commission`, `payout`, `conversion`, `invoice`).
</ResponseField>

<ResponseField name="aggregateId" type="string">
  ID do agregado (ex.: `comm_...`, `pay_...`). Combinado com `aggregateType`, é o eixo natural de auditoria de um recurso.
</ResponseField>

<ResponseField name="payload" type="object">
  Snapshot do fato: um diff (`before`/`after`), a entidade completa ou campos específicos, conforme o tipo. Dinheiro sempre em centavos (inteiro); percentuais em basis points.
</ResponseField>

<ResponseField name="metadata" type="object">
  `{ actor: { type, id }, source? }`. `actor.type` é um de `user`, `api_key` ou `system`: quem causou o fato.
</ResponseField>

<ResponseField name="occurredAt" type="string">
  Instante em que o fato ocorreu, em ISO-8601 (definível pelo emissor).
</ResponseField>

<ResponseField name="recordedAt" type="string">
  Instante em que o evento foi gravado no store, em ISO-8601.
</ResponseField>

## As regras do event store

<AccordionGroup>
  <Accordion title="Imutável por construção">
    O log só recebe novos eventos: um evento, uma vez gravado, nunca é alterado nem apagado. Não existe caminho na API que reescreva o log. Isso é o que torna a trilha de auditoria confiável: nada é editado retroativamente.
  </Accordion>

  <Accordion title="Schema versionado e taxonomia estável">
    Todo evento carrega `type` namespaced, `version` de schema do payload, `aggregateType` + `aggregateId`, `payload`, `metadata` e `occurredAt`/`recordedAt`. O catálogo de tipos é estável e é a fonte do `GET /events/types` e da validação de assinaturas de Webhook. Quando o shape de um payload precisar mudar de forma incompatível, o `version` daquele tipo é incrementado: consumidores leem `version` para saber como interpretar o `payload`.
  </Accordion>

  <Accordion title="Estado e evento sempre juntos">
    Toda mudança de estado e o(s) evento(s) correspondente(s) são gravados de forma atômica: ou os dois entram, ou nenhum entra. Não existe estado sem evento, nem evento sem o estado que ele descreve, exceto a reconciliação, que grava eventos que *são* o próprio fato (uma divergência ou um resumo).
  </Accordion>

  <Accordion title="Log como recurso de API">
    O log é exposto via `GET /events`, escopado pela sua organização, com filtros por tipo, agregado, ator e período, e paginação por cursor em ordem decrescente (mais recente primeiro). É a porta de acesso programático ao histórico, combinada com [Webhooks](/docs/webhooks/visao-geral) para entrega push.
  </Accordion>
</AccordionGroup>

<Note>
  Gravar estado e evento de forma atômica garante que o log nunca diverge do estado por uma falha durante a escrita. A reconciliação (mais abaixo) cobre o caso residual: divergências em que estado e evento ficam inconsistentes entre si.
</Note>

## Consultando o log

O event store é exposto por três endpoints, todos autenticados e escopados pela sua organização (veja [Autenticação](/docs/autenticacao)). Para a referência completa de cada um, veja a aba Referência da API.

| Endpoint                | Descrição                                                            |
| ----------------------- | -------------------------------------------------------------------- |
| `GET /events`           | Lista o log da organização, com filtros e paginação por cursor.      |
| `GET /events/types`     | Taxonomia de eventos com a `version` vigente do schema de cada tipo. |
| `GET /events/{eventId}` | Envelope completo de um evento (payload + metadata).                 |

### Listar eventos

`GET /events` pagina por cursor em ordem **decrescente** (mais recente primeiro). Use os filtros de querystring para recortar a auditoria:

<ParamField query="type" type="string">
  Um único tipo (ex.: `commission.paid`).
</ParamField>

<ParamField query="types" type="string[]">
  Múltiplos tipos (repetível: `?types=commission.paid&types=commission.voided`).
</ParamField>

<ParamField query="aggregate_type" type="string">
  Tipo do agregado (ex.: `commission`).
</ParamField>

<ParamField query="aggregate_id" type="string">
  ID do agregado: o eixo para auditar um recurso específico (ex.: `comm_...`).
</ParamField>

<ParamField query="actor_type" type="string">
  Quem causou o fato: `user`, `api_key` ou `system`.
</ParamField>

<ParamField query="occurred_since" type="string">
  Início do período (inclusivo).
</ParamField>

<ParamField query="occurred_until" type="string">
  Fim do período (exclusivo).
</ParamField>

A paginação segue a convenção da plataforma (`limit` 1 a 100, default 25; `starting_after`/`ending_before`). Veja [Paginação](/docs/convencoes/paginacao).

<CodeGroup>
  ```bash Listar eventos recentes theme={null}
  curl https://api.userepass.com/events \
    -H "Authorization: Bearer rstr_..."
  ```

  ```bash Auditar uma comissão específica theme={null}
  curl "https://api.userepass.com/events?aggregate_type=commission&aggregate_id=comm_01J8Z3K9QH4N2YV7X6PM3T0ABC" \
    -H "Authorization: Bearer rstr_..."
  ```

  ```bash Conversões num período theme={null}
  curl "https://api.userepass.com/events?types=conversion.created&types=conversion.refunded&occurred_since=2026-06-01T00:00:00Z&occurred_until=2026-06-13T00:00:00Z" \
    -H "Authorization: Bearer rstr_..."
  ```
</CodeGroup>

### Buscar um evento

```bash theme={null}
curl https://api.userepass.com/events/evt_01J8Z3K9QH4N2YV7X6PM3T0XYZ \
  -H "Authorization: Bearer rstr_..."
```

```json theme={null}
{
  "id": "evt_01J8Z3K9QH4N2YV7X6PM3T0XYZ",
  "type": "commission.paid",
  "version": 1,
  "aggregateType": "commission",
  "aggregateId": "comm_01J8Z3K9QH4N2YV7X6PM3T0ABC",
  "payload": {
    "amountCents": 1250,
    "payoutId": "pay_01J8Z3K9QH4N2YV7X6PM3T0DEF"
  },
  "metadata": {
    "actor": { "type": "system", "id": "payout-cycle" },
    "source": "payout.completed"
  },
  "occurredAt": "2026-06-12T18:30:00.000Z",
  "recordedAt": "2026-06-12T18:30:00.142Z"
}
```

<Tip>
  O envelope retornado pelo `GET /events/{id}` é exatamente o mesmo body entregue nos [Webhooks](/docs/webhooks/visao-geral). Como a entrega de Webhook é *at-least-once*, o consumidor deve deduplicar pelo `id` do evento, o mesmo `id` que você vê aqui.
</Tip>

### Descobrir a taxonomia

`GET /events/types` retorna o catálogo estático de tipos com a `version` vigente de cada um. Use-o para descobrir o que pode ser auditado e para validar quais tipos você quer assinar num endpoint de Webhook.

```bash theme={null}
curl https://api.userepass.com/events/types \
  -H "Authorization: Bearer rstr_..."
```

O catálogo cobre **toda** a taxonomia da plataforma, não só eventos de Webhook. Os tipos espelham o ciclo de vida de cada módulo: [Programas e regras](/docs/conceitos/programas-e-regras), [Afiliados](/docs/conceitos/afiliados), [Links e cupons](/docs/conceitos/links-e-cupons), [Tracking](/docs/conceitos/tracking), [Conversões e fraude](/docs/conceitos/conversoes-e-fraude), [Comissões](/docs/conceitos/comissoes), [Payouts](/docs/conceitos/payouts), [Fiscal](/docs/conceitos/fiscal) e [Reprocessamento](/docs/conceitos/reprocessamento). A lista completa, com payloads, está no [catálogo de eventos](/docs/webhooks/catalogo-de-eventos).

<Note>
  `webhook.test` é um tipo **sintético**: ele é entregue por uma entrega de teste de Webhook, mas **nunca** é gravado no event store nem aparece em `GET /events/types`. Por isso não pode ser assinado.
</Note>

## Reconstruindo decisões

Como o log preserva, em ordem, cada fato com seu ator e seu snapshot de payload, você consegue reconstruir a sequência completa de decisões sobre um recurso. Filtre por `aggregate_type` + `aggregate_id` e leia os eventos em ordem cronológica.

Por exemplo, o ciclo de vida típico de uma Comissão emerge dos seus eventos:

```mermaid theme={null}
stateDiagram-v2
    [*] --> created: commission.created
    created --> review: commission.review_required
    review --> created: commission.review_cleared
    created --> approved: commission.approved
    approved --> paid: commission.paid
    created --> voided: commission.voided
    approved --> voided: commission.voided
    paid --> clawback: commission.clawback_created
    voided --> [*]
    paid --> [*]
```

Cada transição é um evento com o `actor` que a causou (um processo automático `system`, um operador `user` ou uma `api_key`) e o snapshot do estado naquele instante. Para entender *por que* um valor mudou (por exemplo, após uma alteração de regra), combine o log com o [Reprocessamento](/docs/conceitos/reprocessamento): o evento `reprocess.commission_recalculated` carrega o `before`/`after` do recálculo.

## Reconciliação entre estado e eventos

Gravar estado e evento de forma atômica garante consistência contra falhas durante a escrita. Para cobrir o risco residual (uma divergência em que estado e evento ficam inconsistentes entre si), uma **reconciliação** periódica recalcula o estado esperado dos agregados de dinheiro a partir do log e o compara com o estado armazenado.

```mermaid theme={null}
sequenceDiagram
    participant R as Reconciliação
    participant DB as Estado armazenado
    participant ES as Event store
    loop commission, payout, conversion, invoice
        R->>ES: ler eventos do agregado
        R->>R: derivar estado esperado do log
        R->>DB: comparar com o estado armazenado
    end
    alt divergência
        R->>ES: emite reconciliation.divergence_found
    end
    R->>ES: emite reconciliation.completed (por organização)
```

A reconciliação cobre os agregados de dinheiro (`commission`, `payout`, `conversion` e `invoice`):

* **Comissão**: compara status e `amountCents`.
* **Payout**: compara status e `amountCents` (imutável após a criação).
* **Conversão**: compara **só status**. Um refund parcial muda valores com payload próprio, então comparar valor daria falso positivo.
* **Invoice**: compara **só status**.

Quando o estado derivado do log diverge do armazenado, é emitido um evento `reconciliation.divergence_found` (ator `system`/`reconciliation`) com o campo divergente (`status`, `amountCents` ou `orphan`), o `expected` e o `actual`, abrindo um incidente auditável. Um estado sem nenhum evento gera uma divergência `orphan`. Ao final, é emitido um `reconciliation.completed` por organização, com `checked` e `divergenceCount`.

<Warning>
  Os eventos de reconciliação são, eles próprios, gravados no log. `reconciliation.divergence_found` é o incidente: monitore esse tipo (via `GET /events?type=reconciliation.divergence_found` ou um Webhook assinado) para ser alertado de qualquer inconsistência entre o log e o estado.
</Warning>

## Próximos passos

<CardGroup cols={2}>
  <Card title="Catálogo de eventos" icon="list" href="/docs/webhooks/catalogo-de-eventos">
    A taxonomia completa, com cada tipo de evento e o shape do seu payload.
  </Card>

  <Card title="Webhooks: visão geral" icon="webhook" href="/docs/webhooks/visao-geral">
    Receba os eventos do store por push, com assinatura HMAC e retries.
  </Card>

  <Card title="Reprocessamento" icon="rotate" href="/docs/conceitos/reprocessamento">
    Como mudanças de regra recalculam Comissões, e o que isso registra no log.
  </Card>

  <Card title="Refund e clawback" icon="receipt" href="/docs/guias/refund-e-clawback">
    Acompanhe o ciclo de estorno e clawback pela trilha de eventos.
  </Card>
</CardGroup>
