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

# Cron

> Configure tarefas agendadas nas suas funções: sintaxe cron, fusos horários, guard isCron, limites e sincronização no deploy.

## Configuração

Defina schedules de cron diretamente no `config` da sua função. Nenhuma configuração externa é necessária.

```typescript index.ts theme={null}
import { define, z } from "@jelou/functions";

export default define({
  name: "appointment-reminder",
  description: "Sends appointment reminders via WhatsApp",
  input: z.object({}),
  config: {
    cron: [
      { expression: "0 8 * * *", timezone: "America/Guayaquil" },
      { expression: "0 8 * * *", timezone: "America/Bogota" },
    ],
  },
  handler: async (_input, ctx) => {
    if (!ctx.isCron) return { skipped: true };

    ctx.log("Sending reminders", { cron: ctx.trigger.cron });
    const apiKey = ctx.env.get("JELOU_API_KEY");

    const res = await fetch("https://api.jelou.ai/v1/messages/send", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${apiKey}`,
      },
      body: JSON.stringify({
        botId: ctx.bot.id,
        phone: "593987654321",
        message: "Hi, we remind you that you have an appointment tomorrow at 10:00 AM.",
      }),
    });

    return { sent: 1, status: res.status };
  },
});
```

## Testes locais

Inicie o servidor com `jelou functions dev`. Você pode enviar uma requisição HTTP para a função, mas o guard `isCron` a rejeitará porque não é um trigger de cron real:

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST http://localhost:3000 \
    -H "Content-Type: application/json" \
    -d '{}'
  ```

  ```json Response 200 theme={null}
  {
    "skipped": true,
    "reason": "not a cron trigger"
  }
  ```
</CodeGroup>

<Note>
  Não é possível simular um trigger de cron real via curl — a plataforma injeta `ctx.isCron` e a assinatura criptográfica automaticamente. Para testar a lógica de cron, use [`createMockContext({ isCron: true })`](/pt/guias/funcoes/testing) nos seus testes.
</Note>

## Sintaxe

Cada schedule tem dois campos:

| Campo        | Tipo     | Obrigatório | Descrição                         |
| ------------ | -------- | ----------- | --------------------------------- |
| `expression` | `string` | Sim         | Cron padrão de 5 campos           |
| `timezone`   | `string` | Não         | Fuso horário IANA. Padrão: `UTC`. |

### Formato da expressão

```
┌───────────── minuto (0-59)
│ ┌───────────── hora (0-23)
│ │ ┌───────────── dia do mês (1-31)
│ │ │ ┌───────────── mês (1-12)
│ │ │ │ ┌───────────── dia da semana (0-7, 0 e 7 = domingo)
│ │ │ │ │
* * * * *
```

### Exemplos comuns

| Expressão      | Descrição                             |
| -------------- | ------------------------------------- |
| `0 9 * * *`    | Todo dia às 9:00                      |
| `0 9 * * 1-5`  | Segunda a sexta às 9:00               |
| `*/30 * * * *` | A cada 30 minutos                     |
| `0 0 1 * *`    | Primeiro dia de cada mês à meia-noite |
| `0 */2 * * *`  | A cada 2 horas                        |
| `30 14 * * 3`  | Quarta-feira às 14:30                 |

### Fusos horários

Use qualquer zona IANA válida:

```typescript theme={null}
config: {
  cron: [
    { expression: "0 9 * * *", timezone: "America/Guayaquil" },   // Ecuador
    { expression: "0 9 * * *", timezone: "America/Bogota" },      // Colombia
    { expression: "0 9 * * *", timezone: "America/Mexico_City" }, // Mexico
    { expression: "0 9 * * *", timezone: "America/Argentina/Buenos_Aires" },
    { expression: "0 3 * * *" },                                   // UTC by default
  ],
}
```

## Guard `isCron`

Sua função pode receber tanto requisições HTTP quanto triggers de cron. Use `ctx.isCron` para diferenciá-los:

```typescript theme={null}
handler: async (_input, ctx) => {
  if (!ctx.isCron) {
    return { skipped: true, reason: "not a cron trigger" };
  }

  ctx.log("Cron task running", { cron: ctx.trigger.cron });
  return { executed: true };
}
```

<Warning>
  Sem o guard `isCron`, qualquer requisição HTTP para sua função executará a lógica de cron. Sempre inclua essa verificação.
</Warning>

## Como funciona

1. Você define os schedules em `config.cron`
2. Quando você executa `jelou functions deploy`, a plataforma lê sua configuração e cria os schedules
3. Quando um schedule dispara, sua função recebe uma requisição com `ctx.isCron === true` e `ctx.trigger.cron` com a expressão que a acionou
4. A verificação de assinatura criptográfica impede invocações não autorizadas

## Limites

* Máximo de **10** schedules de cron por função
* Exceder esse limite gera um erro em tempo de definição

## Gerenciamento

Os schedules são **declarativos** — são definidos no código e sincronizados a cada deploy. Para modificar um schedule, altere `config.cron` no seu código e faça o redeploy.

Para visualizar os schedules ativos:

```bash theme={null}
jelou functions cron list query-customer
# ▸ Expression     Timezone             Enabled  Last Triggered
# ▸ 0 8 * * *     America/Guayaquil    yes      2 hours ago
# ▸ 0 8 * * *     America/Bogota       yes      2 hours ago
```

## Cron multi-tool

Quando você usa `app()`, cada ferramenta pode ter seus próprios schedules de cron independentes. As requisições de cron são enviadas para a rota específica de cada ferramenta.

```typescript theme={null}
import { app, define, z } from "@jelou/functions";

export default app({
  tools: {
    dailyCleanup: define({
      description: "Cleans up stale records",
      input: z.object({}),
      config: { cron: [{ expression: "0 3 * * *", timezone: "UTC" }] },
      handler: async (_input, ctx) => {
        if (!ctx.isCron) return { skipped: true };
        return { cleaned: true };
      },
    }),
    hourlySync: define({
      description: "Syncs external data",
      input: z.object({}),
      config: { cron: [{ expression: "0 * * * *" }] },
      handler: async (_input, ctx) => {
        if (!ctx.isCron) return { skipped: true };
        return { synced: true };
      },
    }),
  },
});
```

<Warning>
  O limite de **10** schedules de cron é **agregado** entre todas as ferramentas de um `app()`. Se uma ferramenta usar 6 schedules, as demais ferramentas só poderão usar 4 no total.
</Warning>

## Problemas comuns

<Tabs>
  <Tab title="Cron não dispara">
    Os schedules de cron são sincronizados no deploy. Se você alterou a expressão cron, precisa fazer o redeploy:

    ```bash theme={null}
    jelou functions deploy
    ```

    Verifique se o schedule está ativo:

    ```bash theme={null}
    jelou functions cron list my-function
    # ▸ Expression     Timezone             Enabled  Last Triggered
    # ▸ 0 8 * * *     America/Guayaquil    yes      2 hours ago
    ```

    Se a coluna `Enabled` mostrar `no`, verifique se a expressão cron é válida.
  </Tab>

  <Tab title="Lógica de cron executa via HTTP">
    Sem o guard `isCron`, qualquer requisição HTTP executará a lógica de cron. Adicione a verificação no início do handler:

    ```typescript theme={null}
    handler: async (_input, ctx) => {
      if (!ctx.isCron) return { skipped: true };

      // your cron logic here
      return { executed: true };
    }
    ```

    Isso retorna `{ skipped: true }` para requisições HTTP normais e só executa a lógica quando é um trigger de cron real.
  </Tab>
</Tabs>

<Tip>
  Consulte o [guia completo de multi-tool](/pt/guias/funcoes/multi-tool) para mais detalhes sobre `app()`.
</Tip>
