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

# Optional: Enable Scheduled Automation

> Optional: drive /api/cron/tick from AWS EventBridge on multi-replica deployments. On a single replica the in-process maintenance scheduler already runs the tick every minute, so alerts and scheduled reports fire without any external cron.

<Note>
  **This step is optional.**

  Once `CRON_SECRET` (or its alias `CRON_TOKEN`) is set, KaireonAI's
  **in-process maintenance scheduler** already calls `/api/cron/tick` every
  minute from inside the API container, so alert rules evaluate and scheduled
  reports fire automatically on a single-replica deployment — **no external cron
  wiring required**. Disable it with `MAINTENANCE_SCHEDULER_ENABLED=false`.

  Wire AWS EventBridge (this page) when you run **multiple API replicas** and
  want one external trigger instead of the in-process one — disable the
  in-process scheduler on every replica so ticks don't run in duplicate — or when
  your platform standard is to drive all cron from EventBridge. A reusable
  Terraform module lives at `terraform/modules/eventbridge`; the manual AWS
  Console steps below do the same thing by hand.
</Note>

## What this wires up

The platform's cron endpoint — `POST /api/cron/tick` — drives **two**
subsystems when called:

* **Alert rule evaluation** — metric threshold checks with notification fan-out
  (see [Alert Rules](../../operations-reporting/alert-rules)).
* **Report schedule execution** — render PDF/CSV, narrate via LLM, deliver to
  configured destinations (see [Report Schedules](../../operations-reporting/report-schedules)).

A single EventBridge Scheduler rule to this endpoint is enough for both — the
tick response reports counts for each subsystem.

## What still works without EventBridge

* **Alert rules** — defined in `/settings/alerts`, stored in the database, and
  invokable manually by hitting `/api/cron/tick` with a valid `x-cron-token`.
  Rules stay dormant until evaluated.
* **Notification destinations** — created and tested via the **Test** button
  in `/settings/integrations → Notifications`; fire on demand via
  `POST /api/v1/notifications/send`.
* **Report templates** — built in `/settings/reports`, previewed live, and
  executed immediately via the **Run Now** button or
  `POST /api/v1/reports/templates/[id]/run-now`.
* **Report schedules** — created with a `nextRunAt` value and fired
  automatically by the in-process maintenance scheduler's minute-by-minute
  `/api/cron/tick` on a single replica. Use **Run Now** any time to fire one
  immediately. Only when you disable the in-process scheduler (multi-replica)
  does automatic firing depend on the external trigger set up below.
* **Dashboard Export** — every dashboard's **Export** dropdown (PDF / CSV /
  Markdown / HTML) works unconditionally; no cron required.
* **Save as Report** — saves the report template and schedule
  immediately; scheduled firing waits on this page.

## When to enable

Turn EventBridge on when:

* You want alerts to fire without a human triggering the tick.
* You need scheduled reports to arrive on cadence (daily digests, weekly
  executive summaries).
* Pilot guardrails for LLM / notification spend are in place.

***

## Setup (when you're ready)

The rest of this page documents the AWS Console setup by hand. A reusable
Terraform module is also available at `terraform/modules/eventbridge` — it
provisions the scheduler(s), the IAM role they assume, and the SSM parameter
(`/kaireon/<env>/cron-secret`), targeting a Lambda that proxies each schedule
to `/api/cron/tick`. Wiring it manually first is still a good way to verify
token rotation and observability before codifying it.

### Prerequisites

1. KaireonAI deployed with a reachable HTTPS URL (e.g., App Runner URL or
   custom domain).
2. At least one [notification destination](../../operations-reporting/notifications)
   configured.
3. At least one [alert rule](../../operations-reporting/alert-rules) enabled and referencing
   that destination, or at least one [report schedule](../../operations-reporting/report-schedules).
4. `CRON_TOKEN` environment variable set on the API service to a strong
   random value (32+ chars). Example:
   ```bash theme={null}
   openssl rand -hex 32
   ```

### Step 1 — Store the token in AWS Systems Manager Parameter Store

Keeping the token in Parameter Store lets you rotate without redeploying and
lets EventBridge Scheduler reference it via an IAM role.

1. AWS Console → **Systems Manager → Parameter Store → Create parameter**.
2. **Name:** `/kaireonai/prod/cron-token` (pick a path that matches your env conventions).
3. **Type:** Secure string (encrypted at rest in Parameter Store).
4. **KMS key:** default AWS-managed key is fine for most deployments; use a
   customer-managed KMS key if your security policy requires it.
5. **Value:** paste the 32+ character token.
6. Create.

Make sure the API service's environment (`CRON_TOKEN`) reads from the same
parameter — either via App Runner's secret reference or at deploy time.

### Step 2 — Create an EventBridge Scheduler IAM role

The schedule needs permission to invoke HTTPS endpoints and read the token
from Parameter Store.

1. **IAM → Roles → Create role**.
2. **Trusted entity type:** `Custom trust policy`. Paste:
   ```json theme={null}
   {
     "Version": "2012-10-17",
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": { "Service": "scheduler.amazonaws.com" },
         "Action": "sts:AssumeRole"
       }
     ]
   }
   ```
3. Attach an **inline policy** with the minimum permissions:
   ```json theme={null}
   {
     "Version": "2012-10-17",
     "Statement": [
       {
         "Effect": "Allow",
         "Action": "ssm:GetParameter",
         "Resource": "arn:aws:ssm:us-east-1:111122223333:parameter/kaireonai/prod/cron-token"
       }
     ]
   }
   ```
   Replace the 12-digit AWS account ID, region, and the parameter path as needed.
4. **Role name:** e.g., `kaireonai-scheduler-role`.
5. Create.

### Step 3 — Create the EventBridge Scheduler

1. **EventBridge → Scheduler → Schedules → Create schedule**.
2. **Name:** `kaireonai-alert-tick`.
3. **Schedule pattern:** `Recurring schedule`. For a once-per-minute cadence:
   ```
   rate(1 minute)
   ```
   Or use a cron expression for every 5 minutes:
   ```
   cron(*/5 * * * ? *)
   ```
4. **Flexible time window:** `Off` (keep ticks punctual).
5. Click **Next**.
6. **Target API:** `API destinations` is the cleanest option, but the
   *Invoke API Destination* path requires a separate connection plus
   destination setup. The simplest path is the universal HTTP target:
   * **Target type:** *Universal targets* → `API Gateway / HTTP`.
   * **URL:** `https://your-deployment/api/cron/tick`
   * **Method:** POST.
   * **HTTP headers:**
     * `x-cron-token`: `<resolve:ssm:/kaireonai/prod/cron-token>`
     * `Content-Type`: `application/json`
   * **Request body:** empty.
7. **Execution role:** select `kaireonai-scheduler-role` from Step 2.
8. **Retry policy:** up to 3 attempts, 60s retention is reasonable — the
   `/api/cron/tick` endpoint is idempotent (each tick reads current state)
   so retries do not cause duplicate fires beyond what `cooldownMinutes`
   already suppresses.
9. **Dead-letter queue:** optional. Useful for spotting systemic failures.
10. Create schedule.

### Step 4 — Verify

1. Wait one minute.
2. Check App Runner / CloudWatch logs for the service — you should see an
   entry like:
   ```
   cron.tick Tick complete { ok: true, tenantsProcessed: N, ... }
   ```
3. In the platform UI, open **Settings → Alert Rules** — the `Last fired`
   column should update when a rule triggers.

### Rotating the token

1. Generate a new token (`openssl rand -hex 32`).
2. Update the Parameter Store value.
3. Update the API service's `CRON_TOKEN` env (App Runner deploy or
   equivalent).
4. The next EventBridge invocation after both updates are live uses the new
   token.

<Warning>
  Update the service env FIRST, then the Parameter Store value, to avoid a
  window where EventBridge sends a new token the service rejects.
  Alternatively, bump both in a single deployment.
</Warning>

### Cadence guidance

Match the tick cadence to the smallest `windowMinutes` of your alert rules.
A rule with `windowMinutes: 5` can only fire when the tick runs at least
once every 5 minutes; running it every 1 minute gives sub-window
responsiveness without meaningfully changing compute cost (the evaluator
bails quickly when no rule triggers).

| Rule windowMinutes | Recommended tick cadence |
| ------------------ | ------------------------ |
| 1–5                | Every minute             |
| 5–15               | Every 5 minutes          |
| 15–60              | Every 15 minutes         |
| 60+                | Hourly                   |

The matching EventBridge schedule expressions:

```
rate(1 minute)
cron(0/5 * * * ? *)
cron(0/15 * * * ? *)
cron(0 * * * ? *)
```

### Troubleshooting

**EventBridge shows success but rules never fire** — Check CloudWatch logs
for `401` responses on `/api/cron/tick`. The most common cause is the
`x-cron-token` header not being resolved from Parameter Store (the
`<resolve:ssm:...>` syntax requires the IAM role in Step 2).

**Rules fire once then silently stop** — `cooldownMinutes` is preventing
re-fire. Expected behavior. Look for `status: cooldown` in the tick
response.

**Some tenants fail, others succeed** — Per-tenant errors are isolated. The
tick response lists failures in the `errors` array. Debug using the
`requestId` that appears in the service logs.
