> For the complete documentation index, see [llms.txt](https://docs.skail.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.skail.dev/construir/padroes-e-antipadroes/regua-de-lembretes.md).

# Régua de lembretes

Uma sequência de mensagens em datas definidas (três dias antes do vencimento, no dia, três dias depois), cancelada assim que o que se espera acontece (o pagamento). No skail é uma function com uma espera e vários prazos.

## Quando usar

Cobrança (lembretes antes e depois do vencimento), onboarding (mensagens no dia 1, 3, 7), renovação de contrato, documentos pendentes, carrinho abandonado. Sem o skail isso é uma tabela de agendamentos, um job que roda a cada hora e uma lógica para cancelar o que ficou obsoleto. Aqui, a régua é a sequência de `Delay`s, e o cancelamento é o evento vencendo a corrida.

## O padrão

```csharp
public static class Eventos
{
    public const string PagamentoRecebido = "PAGAMENTO_RECEBIDO";
}

public record Etapa(TimeSpan Quando, string Mensagem);

[SkailFunction]
public async SkailTask ReguaDeCobranca(Guid faturaId, DateTime vencimento)
{
    var agora = await ObterDataAtual();                                       // command

    var regua = new[]
    {
        new Etapa(vencimento.AddDays(-3) - agora, "vence em 3 dias"),
        new Etapa(vencimento             - agora, "vence hoje"),
        new Etapa(vencimento.AddDays(3)  - agora, "venceu há 3 dias"),
        new Etapa(vencimento.AddDays(10) - agora, "venceu há 10 dias"),
    };

    // uma única espera pelo pagamento, compartilhada por todas as etapas
    var pagamento = SkailTask.WaitForEvent<Pagamento>(Eventos.PagamentoRecebido, faturaId.ToString());

    var decorrido = TimeSpan.Zero;
    foreach (var etapa in regua)
    {
        var ateAEtapa = etapa.Quando - decorrido;
        if (ateAEtapa > TimeSpan.Zero)
        {
            var espera = SkailTask.Delay(ateAEtapa);
            if (await SkailTask.WhenAny(pagamento, espera) == pagamento)
            {
                await RegistrarPagamento(faturaId, await pagamento);          // pagou: régua cancelada
                return;
            }
            decorrido = etapa.Quando;
        }

        await EnviarLembrete(faturaId, etapa.Mensagem);                       // command
    }

    // régua esgotada sem pagamento: espera final antes de encaminhar
    var prazoFinal = SkailTask.Delay(TimeSpan.FromDays(5));
    if (await SkailTask.WhenAny(pagamento, prazoFinal) == pagamento)
    {
        await RegistrarPagamento(faturaId, await pagamento);
        return;
    }
    await EncaminharParaCobrancaTerceirizada(faturaId);
}

[SkailCommand]
public async SkailTask<DateTime> ObterDataAtual() => await Task.FromResult(DateTime.UtcNow);
```

Como funciona. O relógio é lido uma vez, em command, e todos os intervalos são calculados a partir dele: determinístico. A espera pelo pagamento é criada uma vez e reutilizada em cada `WhenAny`; quando o pagamento chega, em qualquer ponto da régua, ele vence a corrida e a function encerra sem enviar o próximo lembrete. `decorrido` acumula o tempo já esperado para que cada `Delay` seja o intervalo até a próxima etapa, não desde o início. Etapas cujo momento já passou quando a function inicia (`ateAEtapa <= 0`) são enviadas imediatamente, em sequência.

Quem dispara o pagamento é o webhook do gateway ou a conciliação bancária, com `POST /api/v1/fire/PAGAMENTO_RECEBIDO/{faturaId}`.

## Variações

Régua fixa a partir do início (onboarding): `Quando` é `TimeSpan.FromDays(1)`, `FromDays(3)`, `FromDays(7)`, sem depender de `vencimento`, e o evento de cancelamento é `USUARIO_ATIVOU`.

Régua por canal: cada etapa carrega o canal (e-mail, WhatsApp, ligação) e o command `EnviarLembrete` decide como enviar. O command é idempotente pelo par `faturaId` e etapa, para não enviar duas vezes se falhar depois do envio.

Régua que muda com o valor: uma fatura acima de um limite pula direto para contato humano após o vencimento. A decisão é um `if` sobre um argumento: determinístico.

## O que observar

No Monitor, cada lembrete enviado é um command na linha do tempo, com a data; a espera mostra até quando o próximo `Delay` vai. Um pagamento que chegou aparece como o evento que encerrou a execução. Se a régua terminou sem pagamento, `EncaminharParaCobrancaTerceirizada` é o último item, e é fácil listar as faturas que chegaram lá.

## O que não fazer

Calcular datas com `DateTime.UtcNow` na function. Criar um `WaitForEvent` novo a cada etapa com o mesmo nome e id (o pagamento atenderia a primeira espera, que já perdeu a corrida, e a etapa corrente não veria). Enviar o lembrete direto na function.

## Próximos passos

[Timeout e prazo](/construir/padroes-e-antipadroes/timeout-e-prazo.md) para prazos em data. [Faturamento](/aprender/exemplos-completos/faturamento-completo-cobranca-recorrencia-e-split-de-pagamento.md) é o exemplo completo com cobrança, retries e período de graça. [Tempo e agendamento](/aprender/fundamentos/tempo-e-agendamento.md).
