---
title: Triggers (entry points)
path: concepts/triggers
status: published
---

# Triggers (entry points)

How an invocation begins. The three entry node kinds (`entry_api`, `entry_webhook`, `entry_schedule`) compile to top-level `IRTrigger` declarations on `IRModule.triggers`, NOT to blocks inside `flow.body`. The runtime walks the trigger list to know which inbound events should kick off which flows.

## The closed set

`IRTrigger.kind ∈ {api, webhook, schedule}` — these are the only three values ScaiCore's parser accepts. There is no fourth kind. In particular:

- **There is no `chat` trigger.** Chat reachability is a *publish-time* concern handled by `flow.config.publish_as_model`. When `true`, the deploy step calls `POST /v1/modules/scaicore/cores/{id}/publish`, which registers a `FrontendModel` reachable via `/v1/chat/completions` at slug `scaicore/{tenant}/{core.slug}`. See [Publish as model](./publish-as-model).
- **There is no `message` trigger.** A documentation example in ScaiGrid's manifest YAML once showed `trigger: {type: message, ...}` — that was a doc bug; the YAML loader never honored it.

## Per-kind shape

### `entry_api`

```jsonc
{ "type": "entry_api", "config": { "method": "POST", "path": "/" } }
```

After deploy, invoked via:

```bash
curl -X POST "https://scaigrid.scailabs.ai/v1/modules/scaicore/cores/${CORE_ID}/invoke" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"input": {"message": "hello"}}'
```

The payload's `input` becomes the trigger's emitted scope (visible as `out_request` on the downstream port).

### `entry_webhook`

```jsonc
{ "type": "entry_webhook", "config": { "method": "POST", "path": "/", "auth": null } }
```

Same wire shape as `entry_api`. The distinction is mostly documentary — `webhook` flags that the source is an external system (Stripe, GitHub, etc.) rather than first-party application code. The `auth` field is reserved for future signature-verification metadata.

### `entry_schedule`

```jsonc
{ "type": "entry_schedule", "config": { "cron": "0 * * * *", "timezone": "UTC" } }
```

Standard 5-field cron. The runtime fires the trigger in the configured timezone — pick `UTC` for cross-region deployments unless you have a specific reason not to.

## Multiple entries per flow

Nothing stops a single flow from having multiple entries. The YAML emitter surfaces the first entry as the canonical trigger and adds extra entries as additional flow declarations with the same step list. The IR codegen creates one `IRTrigger` per entry, all pointing to the same `target_flow` name.

In practice, multiple entries are unusual — most flows have one input shape they're built around. If you find yourself adding a second entry of a different kind to the same flow, consider whether you actually want two flows that share a sub-flow (see [Sub-flows](./sub-flows)).
