> ## 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.

# Reprocessar mudança de regra

> Recalcule comissões após alterar uma regra, com segurança.

Regras de comissão são versionadas e imutáveis: criar uma nova versão vale **só para o futuro**. Para aplicar uma versão nova ao passado (recalcular comissões que já foram calculadas com a regra antiga) você cria um **job de reprocessamento**. Este guia mostra o fluxo seguro de ponta a ponta: criar o job (que nasce como *dry-run*), revisar o relatório de impacto, executar e entender como os deltas viram ajustes.

Antes de começar, vale entender os conceitos por trás do mecanismo em [Reprocessamento](/docs/conceitos/reprocessamento) e [Programas e regras](/docs/conceitos/programas-e-regras).

## Quando usar

O caso típico: uma comissão foi calculada com a regra vigente na época (ex.: 20%); depois você publica uma nova versão da regra (ex.: 30%) e quer que ela valha retroativamente para um intervalo de conversões.

Mudar a regra **não** recalcula nada sozinho. O reprocessamento existe justamente para tornar esse recálculo retroativo **explícito, auditável e reversível na decisão** (você sempre aprova antes de aplicar).

<Warning>
  Comissões já **pagas** (`paid`) são imutáveis e **nunca** são reescritas. O reprocessamento não altera dinheiro que já saiu: o delta de uma comissão paga vira uma **comissão de ajuste** (`adjustment`) que entra no netting do próximo payout, positiva ou negativa. Veja [Ajustes sobre comissões pagas](#ajustes-sobre-comissoes-pagas).
</Warning>

## Pré-requisitos

* Papel `owner` ou `admin` na organização. Membros comuns têm acesso somente de leitura ao histórico de jobs (criar, executar e cancelar exigem `admin`+; caso contrário, `403 not_allowed`).
* A nova versão da regra de comissão já criada no programa (um `cmrl_...`). Para publicar uma nova versão, veja a aba Referência da API do endpoint de regras de comissão.
* O intervalo de datas que você quer reprocessar, sobre o `occurredAt` das conversões.

## Ciclo de vida do job

A execução é **síncrona, numa única transação**: não há estado `processing` intermediário. O job nasce em `dry_run` e termina em `executed` ou `canceled`.

```mermaid theme={null}
stateDiagram-v2
    [*] --> dry_run: POST /reprocess (admin+)<br/>recálculo SEM tocar estado
    dry_run --> executed: POST /reprocess/{id}/execute (admin+)<br/>aplica o relatório aprovado
    dry_run --> canceled: POST /reprocess/{id}/cancel (admin+)
    executed --> [*]
    canceled --> [*]
```

<Note>
  No máximo **um** job aberto (`dry_run`) por programa de cada vez. Enquanto houver um dry-run pendente, tentar criar outro no mesmo programa retorna `409 conflict`. Execute ou cancele o job atual para liberar o slot. Jobs em programas diferentes podem rodar em paralelo.
</Note>

## Fluxo passo a passo

<Steps>
  <Step title="Crie o job de reprocessamento (dry-run)">
    `POST /reprocess` cria o job e produz o relatório de impacto **sem tocar em nenhuma comissão**. O corpo aceita apenas `dryRun: true` (default); enviar `dryRun: false` resulta em `400`. A aplicação real é sempre o passo separado do `execute`.

    No momento do create, a regra escolhida é **congelada** num snapshot (`ruleSnapshot`): o execute aplica esse snapshot, não a regra "ao vivo". O intervalo é **meio-aberto** `[from, to)` (`from` inclusivo, `to` exclusivo) sobre o `occurredAt` da conversão.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.userepass.com/reprocess \
        -H "Authorization: Bearer rstr_..." \
        -H "Content-Type: application/json" \
        -H "Idempotency-Key: $(uuidgen)" \
        -d '{
          "programId": "prog_01J9Z8K2QF8M3N4P5R6S7T8X9V",
          "newRuleId": "cmrl_01J9ZB3M4N5P6Q7R8S9T0X1V2W",
          "from": "2026-01-01T00:00:00Z",
          "to": "2026-04-01T00:00:00Z",
          "affiliateIds": ["aff_01J9ZC4N5P6Q7R8S9T0X1V2W3Y"]
        }'
      ```

      ```json Resposta 201 theme={null}
      {
        "id": "repr_01J9ZD5P6Q7R8S9T0X1V2W3X4Y",
        "programId": "prog_01J9Z8K2QF8M3N4P5R6S7T8X9V",
        "status": "dry_run",
        "newRuleId": "cmrl_01J9ZB3M4N5P6Q7R8S9T0X1V2W",
        "from": "2026-01-01T00:00:00Z",
        "to": "2026-04-01T00:00:00Z",
        "affiliateIds": ["aff_01J9ZC4N5P6Q7R8S9T0X1V2W3Y"],
        "confirmationRequired": true,
        "confirmationToken": "rcft_01J9ZD5P6Q7R8S9T0X1V2W3X4Z",
        "summary": {
          "conversionsAffected": 124,
          "commissionsRecalculated": 118,
          "adjustmentsPlanned": 6,
          "blockedCount": 0,
          "unchangedCount": 12,
          "affiliatesImpacted": 1,
          "deltaTotalCents": 248500,
          "topVariations": [
            { "affiliateId": "aff_01J9ZC4N5P6Q7R8S9T0X1V2W3Y", "deltaCents": 248500 }
          ]
        }
      }
      ```
    </CodeGroup>

    <Info>
      `affiliateIds` é opcional: omita para reprocessar **todos** os afiliados do programa. Se enviar, precisa de pelo menos 1 elemento, e cada afiliado deve pertencer ao programa (`400` caso contrário).
    </Info>

    Apenas comissões `standard` **não anuladas** (não `voided`) das conversões no intervalo entram no escopo. Comissões dos tipos `clawback` e `bonus` ficam de fora (são fatos derivados, não recalculáveis pela regra).
  </Step>

  <Step title="Revise o relatório de impacto">
    O campo `summary` (congelado no create) é o relatório que você aprova antes de aplicar. Releia o job a qualquer momento com `GET /reprocess/{id}` e inspecione comissão a comissão com `GET /reprocess/{id}/items`.

    | Campo do `summary`        | Significado                                                                                   |
    | ------------------------- | --------------------------------------------------------------------------------------------- |
    | `conversionsAffected`     | Conversões distintas com pelo menos um item de mudança                                        |
    | `commissionsRecalculated` | Comissões não pagas que mudariam de valor (sofrerão `UPDATE` direto)                          |
    | `adjustmentsPlanned`      | Comissões pagas cujo delta virará uma comissão de ajuste                                      |
    | `blockedCount`            | Comissões reservadas em payout aberto, **impedem o execute**                                  |
    | `unchangedCount`          | Comissões cujo recálculo deu delta 0 (não geram item nem evento)                              |
    | `affiliatesImpacted`      | Afiliados distintos com algum item                                                            |
    | `deltaTotalCents`         | Soma dos deltas **aplicáveis** (exclui `blocked`), em centavos; base do limiar de confirmação |
    | `topVariations`           | Até 5 entradas `{ affiliateId, deltaCents }`, por maior `\|deltaCents\|` (exclui `blocked`)   |

    Cada item (`GET /reprocess/{id}/items`) traz `before`/`after` por comissão e um `kind`:

    | `kind`        | Significado                                                                          | O que acontece no execute                       |
    | ------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------- |
    | `recalculate` | Comissão **não paga** e não reservada (`payoutId` nulo, status `pending`/`approved`) | `UPDATE` direto de `amountCents`                |
    | `adjustment`  | Comissão **paga** (`paid`, imutável)                                                 | Cria uma comissão nova `adjustment` com o delta |
    | `blocked`     | Comissão **reservada em payout aberto** (tem `payoutId`, ainda não `paid`)           | Bloqueia o execute por completo                 |

    ```bash cURL theme={null}
    curl https://api.userepass.com/reprocess/repr_01J9ZD5P6Q7R8S9T0X1V2W3X4Y/items?limit=50 \
      -H "Authorization: Bearer rstr_..."
    ```

    <Warning>
      Se o relatório tiver qualquer item `blocked`, o execute é recusado com `409 conflict` **antes de qualquer escrita**. A comissão está reservada num payout aberto: liquide ou cancele esse payout e crie um **novo** dry-run. Veja [Payouts](/docs/conceitos/payouts).
    </Warning>
  </Step>

  <Step title="Execute (aplique o relatório aprovado)">
    `POST /reprocess/{id}/execute` aplica exatamente o relatório aprovado numa única transação. Se `confirmationRequired` for `true`, você deve enviar o `confirmationToken` devolvido no create.

    A confirmação obrigatória dispara quando `|deltaTotalCents|` ultrapassa o limiar da política (`confirmationThresholdCents`, default R\$ 1.000,00 / `100000` centavos). Token ausente ou errado, acima do limiar, retorna `409 conflict` (não `403`, você continua sendo admin, só falta confirmar).

    <CodeGroup>
      ```bash cURL (com confirmação) theme={null}
      curl -X POST https://api.userepass.com/reprocess/repr_01J9ZD5P6Q7R8S9T0X1V2W3X4Y/execute \
        -H "Authorization: Bearer rstr_..." \
        -H "Content-Type: application/json" \
        -H "Idempotency-Key: $(uuidgen)" \
        -d '{ "confirmationToken": "rcft_01J9ZD5P6Q7R8S9T0X1V2W3X4Z" }'
      ```

      ```bash cURL (sem confirmação) theme={null}
      curl -X POST https://api.userepass.com/reprocess/repr_01J9ZD5P6Q7R8S9T0X1V2W3X4Y/execute \
        -H "Authorization: Bearer rstr_..." \
        -H "Content-Type: application/json" \
        -H "Idempotency-Key: $(uuidgen)" \
        -d '{}'
      ```

      ```json Resposta 200 theme={null}
      {
        "id": "repr_01J9ZD5P6Q7R8S9T0X1V2W3X4Y",
        "status": "executed",
        "executedAt": "2026-06-13T14:02:11Z",
        "summary": {
          "conversionsAffected": 124,
          "commissionsRecalculated": 118,
          "adjustmentsPlanned": 6,
          "blockedCount": 0,
          "deltaTotalCents": 248500
        }
      }
      ```
    </CodeGroup>

    <Note>
      Após o execute (ou o cancel), o `confirmationToken` deixa de circular: a resposta o **omite** quando o status não é mais `dry_run`.
    </Note>

    O execute revalida o estado antes de escrever (proteção anti-stale): cada comissão não paga só é atualizada se ainda casar exatamente com o relatório, e cada comissão paga só vira ajuste se continuar `paid` com o mesmo valor. Se **qualquer** comissão mudou entre o dry-run e o execute (refund, aprovação, reserva, pagamento), a transação inteira aborta com `409 conflict` e **rollback total**: nenhum item parcial é aplicado. Nesse caso, crie um novo dry-run e revise de novo.
  </Step>

  <Step title="Confira os ajustes gerados">
    O execute emite eventos novos encadeados ao histórico (o estado anterior nunca é apagado), nesta ordem:

    1. `reprocess.started`
    2. um `reprocess.commission_recalculated` por item, com `before`/`after`/`deltaCents`
    3. um `commission.created` por comissão de ajuste
    4. `reprocess.completed`, com o `summary` e o `notificationMode` da política

    As comissões de ajuste nascem com status `approved`, sem payout, e ficam elegíveis ao próximo payout. Assine os eventos via [Webhooks](/docs/webhooks/visao-geral) ou reconstrua o histórico pelo [Event store](/docs/conceitos/event-store).
  </Step>
</Steps>

## Ajustes sobre comissões pagas

Comissões `paid` são imutáveis (a base de cada cobrança fica congelada: money em centavos inteiros, percentuais em basis points). O reprocessamento não as reescreve: para cada delta de comissão paga, ele cria uma **comissão nova** do tipo `adjustment`.

```mermaid theme={null}
sequenceDiagram
    participant Job as Job (execute)
    participant Paga as Comissão paga (imutável)
    participant Adj as Comissão adjustment
    participant Pay as Próximo payout

    Job->>Paga: lê before.amountCents (não altera)
    Job->>Adj: cria adjustment (delta = after − before)
    Note over Adj: status approved, sem payout,<br/>referencia o job em calculation.reprocess
    Adj->>Pay: entra no netting (positivo ou negativo)
```

Características da comissão de ajuste:

* Tipo `adjustment`, status `approved`, sem `payoutId`.
* Valor igual ao delta (`after.amountCents − before.amountCents`), que pode ser **negativo**, o mesmo mecanismo do clawback.
* Referencia o job de origem em `calculation.reprocess = { jobId, originalAmountCents, recalculatedAmountCents }`, para auditoria.
* Entra no netting do próximo [Payout](/docs/conceitos/payouts), somando ou abatendo o saldo do afiliado.

Para comissões **não pagas** (`recalculate`), o execute faz um `UPDATE` direto de `amountCents` e o `calculation` original permanece intacto.

## Política de reprocessamento

A política da organização controla quando a dupla confirmação é exigida e como afiliados impactados são tratados. Leia com `GET /settings/reprocess-policy` e atualize com `PUT /settings/reprocess-policy` (escrita exige `admin`+).

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PUT https://api.userepass.com/settings/reprocess-policy \
    -H "Authorization: Bearer rstr_..." \
    -H "Content-Type: application/json" \
    -d '{
      "confirmationThresholdCents": 50000,
      "notificationMode": "notify_negative_only"
    }'
  ```

  ```json Resposta 200 theme={null}
  {
    "confirmationThresholdCents": 50000,
    "notificationMode": "notify_negative_only"
  }
  ```
</CodeGroup>

| Campo                        | Default                 | Significado                                                                                                                                      |
| ---------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `confirmationThresholdCents` | `100000` (R\$ 1.000,00) | Acima desse delta (em valor absoluto) o execute exige `confirmationToken`. Mínimo `0`: zero exige confirmação para qualquer delta diferente de 0 |
| `notificationMode`           | `notify_always`         | Transparência ao afiliado impactado: `notify_always`, `notify_negative_only` ou `silent`                                                         |

O modo de notificação escolhido é registrado no payload do evento `reprocess.completed`, inclusive `silent`, deixando a decisão auditada.

## Erros comuns

<AccordionGroup>
  <Accordion title="409 conflict: job já aberto no programa">
    Já existe um dry-run pendente nesse programa. Execute ou cancele (`POST /reprocess/{id}/cancel`) o job atual para liberar o slot, ou opere em outro programa.
  </Accordion>

  <Accordion title="409 conflict: confirmação obrigatória">
    O `|deltaTotalCents|` ultrapassa o limiar da política e o `confirmationToken` está ausente ou incorreto. Releia o job em `dry_run` para obter o token e reenvie no corpo do execute.
  </Accordion>

  <Accordion title="409 conflict: itens bloqueados (blocked)">
    Há comissões reservadas num payout aberto. Liquide ou cancele esse payout e crie um novo dry-run.
  </Accordion>

  <Accordion title="409 conflict: relatório desatualizado (anti-stale)">
    Uma comissão mudou entre o dry-run e o execute (refund, aprovação, reserva ou pagamento). A transação aborta por completo; crie um novo dry-run e revise.
  </Accordion>

  <Accordion title="400 parameter_invalid: intervalo ou dryRun inválido">
    `from` deve ser anterior a `to`, e `dryRun` só aceita `true`. Afiliado fora do programa também cai aqui.
  </Accordion>

  <Accordion title="403 not_allowed: papel insuficiente">
    Criar, executar, cancelar ou alterar a política exige `owner` ou `admin`. Membros comuns só têm leitura.
  </Accordion>
</AccordionGroup>

Para o catálogo completo de códigos de erro, veja [Erros](/docs/convencoes/erros).

## Próximos passos

<CardGroup cols={2}>
  <Card title="Reprocessamento" icon="rotate" href="/docs/conceitos/reprocessamento">
    O conceito por trás do recálculo retroativo auditável.
  </Card>

  <Card title="Programas e regras" icon="sliders" href="/docs/conceitos/programas-e-regras">
    Como versionar regras de comissão e por que valem só para o futuro.
  </Card>

  <Card title="Comissões" icon="coins" href="/docs/conceitos/comissoes">
    Cálculo, estados e tipos de comissão (incluindo adjustment).
  </Card>

  <Card title="Refund e clawback" icon="arrow-rotate-left" href="/docs/guias/refund-e-clawback">
    O outro mecanismo que ajusta comissões via netting.
  </Card>
</CardGroup>
