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

# Otomasi

> Jadwalkan pekerjaan dan respons webhook dari dalam aplikasi Kazzle.

Otomasi adalah bagian dari aplikasi yang berjalan sendiri — sesuai jadwal, atau ketika layanan eksternal mengirim acara.

Anda mendeklarasikannya pada komponen process di `kazzle.config.ts`. Satu komponen dapat memiliki sebanyak trigger yang Anda butuhkan.

## Bentuknya

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

Dua hal terjadi di sini:

* `processMode` memilih **siklus hidup** — server yang berjalan lama, atau satu kali berjalan per trigger.
* `triggers[]` mencantumkan **acara** yang harus memicu komponen ini.

Kedua bagian tersebut independen. Server yang persisten dapat memiliki cron. Proses yang efemeral dapat memiliki webhook. Pilih siklus hidup yang sesuai dengan beban kerja, kemudian lampirkan sebanyak trigger yang Anda inginkan.

## `processMode`

| Mode                   | Yang berjalan                                               | Kapan digunakan                                                                                                         |
| ---------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `persistent` (default) | Server HTTP yang berjalan lama. Trigger diPOST ke dalamnya. | Komponen sudah melayani HTTP, atau menyimpan status dalam memori (antrian, websocket, cache).                           |
| `triggered`            | Skrip entri dijalankan per trigger dan keluar.              | Pekerjaan latar belakang murni — pembersihan malam hari, penanganan webhook Stripe tunggal, dll. Tidak ada server idle. |

## Trigger

Setiap trigger memiliki `name` (unik dalam komponen), `kind`, dan — tergantung mode — `schedule` dan/atau `path`.

| Field      | Kapan diperlukan                   | Catatan                                                         |
| ---------- | ---------------------------------- | --------------------------------------------------------------- |
| `name`     | selalu                             | Digunakan sebagai segmen URL webhook dan dalam log. Kebab-case. |
| `kind`     | selalu                             | `'schedule'` atau `'webhook'`.                                  |
| `schedule` | ketika `kind: 'schedule'`          | Ekspresi cron 5-field. Resolusi menit adalah batas bawah.       |
| `path`     | ketika `processMode: 'persistent'` | Rute HTTP pada server Anda tempat trigger mendarat.             |

## Mode persisten — HTTP ke server

Ketika trigger diaktifkan untuk komponen persisten, Kazzle memPOST ke server Anda di `path` yang dideklarasikan. Permintaan membawa:

| Header                                           | Apa yang diberitahukannya                       |
| ------------------------------------------------ | ----------------------------------------------- |
| `Authorization: Bearer ${KAZZLE_TRIGGER_SECRET}` | Validasi ini. Tolak panggilan yang tidak cocok. |
| `x-kazzle-trigger-name`                          | `name` trigger dari manifest.                   |
| `x-kazzle-trigger-run-id`                        | ID opak untuk korelasi log.                     |
| `x-kazzle-triggered-by`                          | `cron` \| `webhook` \| `manual`.                |

Untuk trigger webhook, badan permintaan asli diteruskan sebagai badan POST. Untuk trigger jadwal, badan kosong.

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (persistent mode)
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 — satu kali per trigger

Ketika trigger diaktifkan untuk komponen `triggered`, Kazzle menjalankan skrip entri segar dan menunggu hingga keluar. Tidak ada `path`; skrip mempelajari trigger mana yang diaktifkan dari variabel env.

| Variabel Env      | Nilai                              |
| ----------------- | ---------------------------------- |
| `TRIGGER_NAME`    | `name` trigger dari manifest.      |
| `TRIGGERED_BY`    | `cron` \| `webhook` \| `manual`.   |
| `RUN_ID`          | ID opak untuk korelasi log.        |
| `WEBHOOK_PAYLOAD` | Badan JSON (webhook trigger saja). |

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

Komponen triggered tidak memiliki mesin idle di produksi — mereka berjalan per panggilan dan mati saat keluar.

## URL Webhook

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

Segmen `triggerName` harus cocok dengan entri `kind: 'webhook'` di `triggers[]` komponen tersebut. Nama trigger yang tidak dikenal mengembalikan 404.

## Resolusi jadwal

Ekspresi cron adalah 5-field (menit, jam, hari-bulan, bulan, hari-minggu) dan resolusi menit adalah batas bawah. Jadwal sub-menit ditolak pada waktu validasi manifest.

## Bagaimana run dicatat

Setiap pemicu fire menulis baris `process_runs` dengan `trigger_name`, `triggered_by`, `run_id`, dan status keluar run. Anda dapat menanyakan ini dari kode Anda sendiri atau memeriksa di tampilan run aplikasi.

## Kehabisan kredit

Run yang gagal dicatat dan dicatat, tetapi jadwal terus berjalan sesuai ritme normalnya — run yang tidak stabil tidak pernah menonaktifkan trigger. Satu-satunya hal yang menghentikan run adalah kredit: setiap pemicu fire diperiksa terhadap saldo space, dan sementara space kehabisan kredit (atau tidak memiliki penagihan yang disiapkan) run dilewati dengan `402`. Ini self-recovering — jadwal tetap aktif dan fire berikutnya setelah Anda mengisi ulang berjalan normal, tanpa resume manual.

## Menambahkan otomasi nanti

Aplikasi sederhana dapat dimulai tanpa trigger dan mendapatkannya nanti — tambahkan ringkasan harian, hubungkan Stripe, jalankan pembersihan. Siklus hidup komponen (`processMode`) dan trigger (`triggers[]`) independen, jadi Anda dapat mengubahnya tanpa menulis ulang sisa aplikasi.
