> 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/construa/casos-de-uso/aprovacao-com-prazo.md).

# Aprovação com prazo

Muitos sistemas precisam lidar com algo simples, mas chato de implementar:

* enviar um e-mail
* esperar o usuário responder
* e seguir o fluxo com base nisso
* ou tomar uma ação automática se ele não responder

No legado, isso normalmente vira uma mistura de:

* tabela de controle
* job rodando de tempos em tempos
* lógica de timeout manual
* retries espalhados

Com o skail, isso vira um código direto simples.

### Esperando aprovação com prazo

Imagine um fluxo de aprovação simples:

1. você envia um e-mail com um link
2. espera o usuário responder
3. se ele aprovar → segue o fluxo
4. se ele rejeitar → notifica a rejeição
5. se não responder em 72h → expira

No skail, isso vira uma função linear:

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
[SkailFunction]
public async SkailTask WaitForApprovalWithTimeout(Guid approvalId)
{
    await SendApprovalEmail(approvalId);

    var approvalTask = SkailTask.WaitForEvent<ApprovalResponse>("EVT_APPROVAL", approvalId.ToString());
    var timeoutTask = SkailTask.Delay(TimeSpan.FromHours(72));

    var result = await SkailTask.WhenAny(approvalTask, timeoutTask);

    if (result == timeoutTask)
    {
        await NotifyExpired(approvalId);
        return;
    }

    var response = await approvalTask;

    if (response.Approved)
        await NotifyApproved(approvalId);
    else
        await NotifyRejected(approvalId);
}

public record ApprovalResponse(bool Approved);
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
{% code lineNumbers="true" %}

```typescript
import { Skail, SkailCommand, SkailFunction } from "@skail/runtime";
import { EmailService, notificacoes } from "./aprovacao-integracoes.js";

export interface ApprovalResponse {
  Approved: boolean;
}

export class Aprovacoes {
  @SkailFunction()
  async WaitForApprovalWithTimeout(approvalId: string): Promise<void> {
    await this.SendApprovalEmail(approvalId);

    const approvalTask = Skail.waitForEvent<ApprovalResponse>("EVT_APPROVAL", approvalId);
    const timeoutTask = Skail.delayMinutes(72 * 60);

    const response = await Skail.whenAny(approvalTask, timeoutTask);

    if (response === undefined) {
      await this.NotifyExpired(approvalId);
      return;
    }

    if (response.Approved) await this.NotifyApproved(approvalId);
    else await this.NotifyRejected(approvalId);
  }

  @SkailCommand()
  async SendApprovalEmail(approvalId: string): Promise<void> {
    const link = `https://portal.company.com/approve/${approvalId}`;
    await EmailService.SendAsync("Approval Required", `Click here to approve: ${link}`);
  }

  @SkailCommand()
  async NotifyApproved(approvalId: string): Promise<void> {
    await notificacoes.Aprovada(approvalId);
  }

  @SkailCommand()
  async NotifyRejected(approvalId: string): Promise<void> {
    await notificacoes.Rejeitada(approvalId);
  }

  @SkailCommand()
  async NotifyExpired(approvalId: string): Promise<void> {
    await notificacoes.Expirada(approvalId);
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### O que está acontecendo aqui

<figure><img src="https://1898946878-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsJmS5ILPG5x2x5j0WyiV%2Fuploads%2FU4UkYBnK6iLThlw4FNRI%2Fap-aprovacao.svg?alt=media" alt="WaitForApprovalWithTimeout em quatro passos: SendApprovalEmail envia o e-mail; a function espera ao mesmo tempo o delay de 72 horas e o evento EVT_APPROVAL; o que terminar primeiro decide o caminho: NotifyExpired se o prazo vencer, NotifyApproved ou NotifyRejected conforme a resposta. Fora da function, o usuário clica no link, o seu endpoint chama POST /api/v1/fire/EVT_APPROVAL/{approvalId} e a espera termina."><figcaption></figcaption></figure>

O fluxo é simples de ler:<br>

1. envia o e-mail
2. espera duas coisas ao mesmo tempo:
   1. o usuário responder
   2. ou, o tempo expirar
3. segue baseado no que acontecer primeiro
4. se a resposta chegou, lê o payload: aprovado segue o fluxo, rejeitado notifica a rejeição

O fluxo dispensa job, polling e tabela de controle, e o código trata só da regra de negócio.

### Enviando o e-mail (sem mudar seu legado)

A chamada para enviar o e-mail continua sendo uma integração normal:

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
[SkailCommand]
public async SkailTask SendApprovalEmail(Guid approvalId)
{
    var link = $"https://portal.company.com/approve/{approvalId}";
    await EmailService.SendAsync("Approval Required", $"Click here to approve: {link}");
}
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
{% code lineNumbers="true" %}

```typescript
import { SkailCommand } from "@skail/runtime";
import { EmailService } from "./aprovacao-integracoes.js";

export class Emails {
  @SkailCommand()
  async SendApprovalEmail(approvalId: string): Promise<void> {
    const link = `https://portal.company.com/approve/${approvalId}`;
    await EmailService.SendAsync("Approval Required", `Click here to approve: ${link}`);
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Ou seja:

* você continua usando seus serviços atuais
* sem precisar reescrever nada

### Como o usuário responde

Quando o usuário clica no link, o endpoint que recebe o clique só precisa disparar o evento na [API HTTP de eventos do skail](/referencia/api-http/post-fire.md). Pode ser o seu backend, um BFF, um webhook, qualquer sistema em qualquer linguagem:

```bash
curl -X POST "https://SEU_SKAIL_BASE_URL/api/v1/fire/EVT_APPROVAL/{approvalId}" \
  -H "Content-Type: application/json" \
  -H "skail-key: SUA_SKAIL_KEY" \
  -H "Skail-namespace: SEU_NAMESPACE" \
  -d '{ "Approved": true }'
```

O nome do evento (`EVT_APPROVAL`) e o identificador (`approvalId`) precisam ser exatamente os mesmos usados na espera pelo evento. É esse par que conecta quem dispara a quem está esperando. O corpo segue o formato de `ApprovalResponse`: com `{ "Approved": false }`, a função segue pelo ramo da rejeição.

Quando o evento chega, a execução continua de onde parou.

### Por que isso é relevante

No modelo tradicional, esse tipo de fluxo costuma ser difícil de manter:

* estado espalhado
* lógica de timeout manual
* difícil de entender o que está acontecendo

Aqui, tudo fica explícito:

* **o fluxo está em um único lugar**
* o estado é controlado automaticamente
* o tempo não depende de job rodando

A espera pode durar dias, e durante esse tempo a execução fica pausada sem consumir recursos. Ao retomar, ela tem todas as variáveis e o contexto de antes, como se nunca tivesse parado.

### Resultado

Você transforma algo que normalmente é:

* complexo
* frágil
* difícil de debugar

...em um fluxo simples de ler e manter.

Sem falar na facilidade de depurar com o Time Travel Debug, como qualquer outro código skail.
