kazzle.config.ts. One component can carry as many triggers as you need.
The shape
processModepicks the lifecycle — long-running server, or one-off run per trigger.triggers[]lists the events that should fire this component.
processMode
| Mode | What runs | When to use |
|---|---|---|
persistent (default) | A long-running HTTP server. Triggers are POSTed into it. | The component already serves HTTP, or holds state in memory (queues, websockets, caches). |
triggered | The entry script is spawned per trigger and exits. | Pure background jobs — nightly cleanup, single Stripe webhook handler, etc. No idle servers. |
Triggers
Each trigger has aname (unique within the component), a kind, and — depending on mode — a schedule and/or path.
| Field | When required | Notes |
|---|---|---|
name | always | Used as the webhook URL segment and in logs. Kebab-case. |
kind | always | 'schedule' or 'webhook'. |
schedule | when kind: 'schedule' | 5-field cron expression. Minute resolution is the floor. |
path | when processMode: 'persistent' | HTTP route on your server where the trigger lands. |
Persistent mode — HTTP into the server
When a trigger fires for a persistent component, Kazzle POSTs to your server at the declaredpath. The request carries:
| Header | What it tells you |
|---|---|
Authorization: Bearer ${KAZZLE_TRIGGER_SECRET} | Validate this. Reject calls that don’t match. |
x-kazzle-trigger-name | The trigger’s name from the manifest. |
x-kazzle-trigger-run-id | Opaque ID for log correlation. |
x-kazzle-triggered-by | cron | webhook | manual. |
Triggered mode — one-off per trigger
When a trigger fires for atriggered component, Kazzle spawns the entry script fresh and waits for it to exit. There is no path; the script learns which trigger fired from env vars.
| Env var | Value |
|---|---|
TRIGGER_NAME | The trigger’s name from the manifest. |
TRIGGERED_BY | cron | webhook | manual. |
RUN_ID | Opaque ID for log correlation. |
WEBHOOK_PAYLOAD | JSON body (webhook triggers only). |
Webhook URLs
triggerName segment must match a kind: 'webhook' entry in that component’s triggers[]. Unknown trigger names return 404.
Schedule resolution
Cron expressions are 5-field (minute, hour, day-of-month, month, day-of-week) and minute resolution is the floor. Sub-minute schedules are rejected at manifest validation time.How runs are recorded
Each trigger fire writes aprocess_runs row with the trigger_name, triggered_by, run_id, and the run’s exit status. You can query these from your own code or inspect them in the app’s runs view.
Running out of credits
A failing run is recorded and logged, but the schedule keeps running on its normal cadence — a flaky run never disables the trigger. The one thing that stops a run is credits: every trigger fire is checked against the space’s balance, and while the space is out of credits (or has no billing set up) runs are skipped with a402. This is self-recovering — the schedule stays armed and the next fire after you top up runs normally, with no manual resume.
Adding automations later
A simple app can start with no triggers and gain them later — add a daily summary, connect Stripe, run cleanup. The component’s lifecycle (processMode) and triggers (triggers[]) are independent, so you can change them without rewriting the rest of the app.