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

# Automations

> Planifiez des tâches et répondez aux webhooks depuis une app Kazzle.

Les automations sont les parties d'une app qui s'exécutent seules — selon un calendrier, ou quand un service externe envoie un événement.

Vous les déclarez sur un composant process dans `kazzle.config.ts`. Un composant peut avoir autant de déclencheurs que nécessaire.

## La structure

```ts theme={"theme":"material-theme-darker"}
{
  name: 'events',
  type: 'process',
  path: './components/events/index.ts',
  processMode: 'persistent', // ou 'triggered'
  triggers: [
    { name: 'daily-digest', kind: 'schedule', schedule: '0 9 * * *', path: '/cron/daily-digest' },
    { name: 'stripe',       kind: 'webhook',                          path: '/webhook/stripe' },
  ],
}
```

Deux choses se produisent ici :

* `processMode` définit le **cycle de vie** — serveur long-running, ou exécution unique par déclencheur.
* `triggers[]` liste les **événements** qui doivent déclencher ce composant.

Les deux éléments sont indépendants. Un serveur persistent peut avoir un cron. Un processus éphémère peut avoir un webhook. Choisissez le cycle de vie qui correspond à votre charge de travail, puis attachez autant de déclencheurs que vous le souhaitez.

## `processMode`

| Mode                      | Ce qui s'exécute                                                | Quand l'utiliser                                                                                              |
| ------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `persistent` (par défaut) | Un serveur HTTP long-running. Les déclencheurs lui sont POSTés. | Le composant sert déjà HTTP, ou maintient un état en mémoire (files d'attente, websockets, caches).           |
| `triggered`               | Le script d'entrée est lancé par déclencheur et se termine.     | Tâches de fond pures — nettoyage nocturne, gestionnaire webhook Stripe unique, etc. Pas de serveurs inactifs. |

## Déclencheurs

Chaque déclencheur a un `name` (unique dans le composant), un `kind`, et — selon le mode — un `schedule` et/ou un `path`.

| Champ      | Quand requis                      | Notes                                                                 |
| ---------- | --------------------------------- | --------------------------------------------------------------------- |
| `name`     | toujours                          | Utilisé comme segment d'URL webhook et dans les logs. Kebab-case.     |
| `kind`     | toujours                          | `'schedule'` ou `'webhook'`.                                          |
| `schedule` | quand `kind: 'schedule'`          | Expression cron à 5 champs. La résolution à la minute est le minimum. |
| `path`     | quand `processMode: 'persistent'` | Route HTTP sur votre serveur où le déclencheur arrive.                |

## Mode persistent — HTTP vers le serveur

Quand un déclencheur se déclenche pour un composant persistent, Kazzle POST vers votre serveur au `path` déclaré. La requête contient :

| En-tête                                          | Ce qu'il vous dit                                          |
| ------------------------------------------------ | ---------------------------------------------------------- |
| `Authorization: Bearer ${KAZZLE_TRIGGER_SECRET}` | Validez ceci. Rejetez les appels qui ne correspondent pas. |
| `x-kazzle-trigger-name`                          | Le `name` du déclencheur du manifeste.                     |
| `x-kazzle-trigger-run-id`                        | ID opaque pour la corrélation des logs.                    |
| `x-kazzle-triggered-by`                          | `cron` \| `webhook` \| `manual`.                           |

Pour les déclencheurs webhook, le corps de la requête d'origine est transféré comme corps POST. Pour les déclencheurs de calendrier, le corps est vide.

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (mode persistent)
const TRIGGER_SECRET = process.env.KAZZLE_TRIGGER_SECRET ?? '';

Bun.serve({
  port: Number(process.env.PORT),
  hostname: process.env.HOST,
  async fetch(req) {
    const url = new URL(req.url);

    if (req.method === 'POST' && url.pathname === '/cron/daily-digest') {
      if (req.headers.get('authorization') !== `Bearer ${TRIGGER_SECRET}`) {
        return new Response('Unauthorized', { status: 401 });
      }
      await sendDigest();
      return Response.json({ ok: true });
    }

    if (req.method === 'POST' && url.pathname === '/webhook/stripe') {
      if (req.headers.get('authorization') !== `Bearer ${TRIGGER_SECRET}`) {
        return new Response('Unauthorized', { status: 401 });
      }
      const event = await req.json();
      await handleStripe(event);
      return Response.json({ ok: true });
    }

    return new Response('not found', { status: 404 });
  },
});
```

## Mode triggered — exécution unique par déclencheur

Quand un déclencheur se déclenche pour un composant `triggered`, Kazzle lance le script d'entrée à neuf et attend sa fermeture. Il n'y a pas de `path` ; le script apprend quel déclencheur s'est déclenché via des variables d'environnement.

| Variable d'env    | Valeur                                        |
| ----------------- | --------------------------------------------- |
| `TRIGGER_NAME`    | Le `name` du déclencheur du manifeste.        |
| `TRIGGERED_BY`    | `cron` \| `webhook` \| `manual`.              |
| `RUN_ID`          | ID opaque pour la corrélation des logs.       |
| `WEBHOOK_PAYLOAD` | Corps JSON (déclencheurs webhook uniquement). |

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (mode triggered)
const trigger = process.env.TRIGGER_NAME;
const runId = process.env.RUN_ID;

if (trigger === 'daily-digest') {
  await sendDigest();
} else if (trigger === 'stripe') {
  const event = JSON.parse(process.env.WEBHOOK_PAYLOAD ?? '{}');
  await handleStripe(event);
}

console.log(`run ${runId} done`);
```

Les composants triggered n'ont pas de machines inactives en production — ils démarrent par appel et s'arrêtent à la fermeture.

## URLs des webhooks

```text theme={"theme":"material-theme-darker"}
POST https://api.kazzle.app/webhooks/{spaceId}/{appId}/{componentName}/{triggerName}
```

Le segment `triggerName` doit correspondre à une entrée `kind: 'webhook'` dans le `triggers[]` de ce composant. Les noms de déclencheurs inconnus retournent 404.

## Résolution du calendrier

Les expressions cron sont à 5 champs (minute, heure, jour du mois, mois, jour de la semaine) et la résolution à la minute est le minimum. Les calendriers sub-minute sont rejetés lors de la validation du manifeste.

## Comment les exécutions sont enregistrées

Chaque déclenchement écrit une ligne `process_runs` avec le `trigger_name`, `triggered_by`, `run_id`, et le statut de sortie de l'exécution. Vous pouvez les interroger depuis votre propre code ou les inspecter dans la vue des exécutions de l'app.

## Manque de crédits

Une exécution échouée est enregistrée et loggée, mais le calendrier continue à s'exécuter selon son cadence normal — une exécution défaillante ne désactive jamais le déclencheur. La seule chose qui arrête une exécution est les crédits : chaque déclenchement est vérifié par rapport au solde de l'espace, et tant que l'espace n'a pas de crédits (ou n'a pas de facturation configurée), les exécutions sont ignorées avec un `402`. C'est auto-récupérable — le calendrier reste armé et le prochain déclenchement après recharge s'exécute normalement, sans reprise manuelle.

## Ajouter des automations plus tard

Une app simple peut commencer sans déclencheurs et en gagner plus tard — ajouter un résumé quotidien, connecter Stripe, exécuter un nettoyage. Le cycle de vie du composant (`processMode`) et les déclencheurs (`triggers[]`) sont indépendants, vous pouvez donc les modifier sans réécrire le reste de l'app.
