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

# Automatiseringen

> Plan taken in en reageer op webhooks vanuit een Kazzle-app.

Automatiseringen zijn de onderdelen van een app die zelfstandig worden uitgevoerd — volgens een schema, of wanneer een externe service een gebeurtenis stuurt.

Je declareert ze op een process-component in `kazzle.config.ts`. Eén component kan zoveel triggers hebben als je nodig hebt.

## De structuur

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

Hier gebeuren twee dingen:

* `processMode` bepaalt de **levenscyclus** — langlopende server, of eenmalige uitvoering per trigger.
* `triggers[]` geeft de **gebeurtenissen** aan die deze component moeten activeren.

De twee onderdelen zijn onafhankelijk. Een persistent server kan een cron hebben. Een eenmalig proces kan een webhook hebben. Kies de levenscyclus die bij je werkbelasting past, en voeg zoveel triggers toe als je wilt.

## `processMode`

| Modus                    | Wat wordt uitgevoerd                                         | Wanneer gebruiken                                                                                              |
| ------------------------ | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| `persistent` (standaard) | Een langlopende HTTP-server. Triggers worden erin geplaatst. | De component serveert al HTTP, of behoudt status in het geheugen (wachtrijen, websockets, caches).             |
| `triggered`              | Het invoerscript wordt per trigger gestart en afgesloten.    | Zuivere achtergrondtaken — nachtelijke opschoning, enkele Stripe-webhook-handler, enz. Geen inactieve servers. |

## Triggers

Elke trigger heeft een `name` (uniek binnen de component), een `kind`, en — afhankelijk van de modus — een `schedule` en/of `path`.

| Veld       | Wanneer vereist                     | Opmerkingen                                              |
| ---------- | ----------------------------------- | -------------------------------------------------------- |
| `name`     | altijd                              | Gebruikt als webhook-URL-segment en in logs. Kebab-case. |
| `kind`     | altijd                              | `'schedule'` of `'webhook'`.                             |
| `schedule` | wanneer `kind: 'schedule'`          | 5-veld cron-expressie. Minuutresolutie is het minimum.   |
| `path`     | wanneer `processMode: 'persistent'` | HTTP-route op je server waar de trigger aankomt.         |

## Persistent modus — HTTP naar de server

Wanneer een trigger voor een persistent component wordt geactiveerd, stuurt Kazzle een POST naar je server op het aangegeven `path`. Het verzoek bevat:

| Header                                           | Wat het je vertelt                                    |
| ------------------------------------------------ | ----------------------------------------------------- |
| `Authorization: Bearer ${KAZZLE_TRIGGER_SECRET}` | Valideer dit. Wijs oproepen af die niet overeenkomen. |
| `x-kazzle-trigger-name`                          | De `name` van de trigger uit het manifest.            |
| `x-kazzle-trigger-run-id`                        | Ondoorzichtige ID voor logcorrelatie.                 |
| `x-kazzle-triggered-by`                          | `cron` \| `webhook` \| `manual`.                      |

Voor webhook-triggers wordt de originele aanvraagbody doorgestuurd als de POST-body. Voor schema-triggers is de body leeg.

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (persistent modus)
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 });
  },
});
```

## Triggered modus — eenmalig per trigger

Wanneer een trigger voor een `triggered`-component wordt geactiveerd, start Kazzle het invoerscript opnieuw op en wacht tot het afsluit. Er is geen `path`; het script leert welke trigger is geactiveerd via omgevingsvariabelen.

| Omgevingsvariabele | Waarde                                     |
| ------------------ | ------------------------------------------ |
| `TRIGGER_NAME`     | De `name` van de trigger uit het manifest. |
| `TRIGGERED_BY`     | `cron` \| `webhook` \| `manual`.           |
| `RUN_ID`           | Ondoorzichtige ID voor logcorrelatie.      |
| `WEBHOOK_PAYLOAD`  | JSON-body (alleen webhook-triggers).       |

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (triggered modus)
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`);
```

Triggered-componenten hebben geen inactieve machines in productie — ze starten per oproep op en sluiten af bij afsluiting.

## Webhook-URL's

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

Het `triggerName`-segment moet overeenkomen met een `kind: 'webhook'`-item in de `triggers[]` van die component. Onbekende triggernamen retourneren 404.

## Schemaresolutie

Cron-expressies zijn 5-veld (minuut, uur, dag-van-maand, maand, dag-van-week) en minuutresolutie is het minimum. Sub-minuutschema's worden afgewezen bij validatie van het manifest.

## Hoe runs worden geregistreerd

Elke triggeractivering schrijft een `process_runs`-rij met de `trigger_name`, `triggered_by`, `run_id` en de afsluitstatus van de run. Je kunt deze opvragen vanuit je eigen code of inspecteren in de runs-weergave van de app.

## Tegoed op

Een mislukte run wordt geregistreerd en gelogd, maar het schema blijft op zijn normale cadans lopen — een onbetrouwbare run schakelt de trigger nooit uit. Het enige wat een run stopt is tegoed: elke triggeractivering wordt gecontroleerd tegen het saldo van de space, en terwijl de space geen tegoed heeft (of geen facturering is ingesteld) worden runs overgeslagen met een `402`. Dit herstelt zichzelf — het schema blijft actief en de volgende activering nadat je het aanvult, wordt normaal uitgevoerd, zonder handmatig hervatten.

## Automatiseringen later toevoegen

Een eenvoudige app kan zonder triggers beginnen en deze later krijgen — voeg een dagelijks overzicht toe, verbind Stripe, voer opschoning uit. De levenscyclus van de component (`processMode`) en triggers (`triggers[]`) zijn onafhankelijk, dus je kunt ze wijzigen zonder de rest van de app te herschrijven.
