---
question: Who schedules and starts a module's code in a NATS project?
kind: overview
---

# Orchestrations

Orchestrating code in ABI means running a module's methods without anyone calling them: on a schedule, when something happens, or in sequence after another step. The unit is the **job**: a module method plus its triggers. The module declares its jobs and runs them in its own process. JetStream stores the schedules, fires them, and hands each trigger to one replica of the module.

There is no central scheduler. Adding, changing or removing a module's jobs needs no restart of anything else: the module publishes its schedules when it starts, and the broker fires them.

<!-- matrix -->

:::note NATS projects only
Jobs need NATS, the default of `abi new project`. In a project without NATS (legacy), a declared job is logged and not run. Such a project can use [Dagster](/capabilities/build/applications/dagster), or [move to NATS](/capabilities/upgrade/migrate-to-3).
:::

## Three ways a job starts

| Start | Declared with | Page |
|---|---|---|
| On a schedule | `Cron("0 30 9 * * mon-fri")`, `Cron("@daily")` or `Every("15m")` | [Schedule a job](/capabilities/build/orchestrations/schedule-a-job) |
| On an event | `OnEvent(event_type=<IRI>, filter={...})` or `OnEvent("<subject>")` | [React to an event](/capabilities/build/orchestrations/react-to-an-event) |
| By hand | No trigger needed: **Run now** in Nexus, `trigger_job` in code, or a REST call | [Run a job by hand](/capabilities/build/orchestrations/run-a-job-by-hand) |

A job can combine them: up to 16 triggers, and it can always be started by hand. Steps that depend on each other are separate jobs, joined by events:

![A scheduled export job writes a CSV to object storage. Object storage emits ObjectPut. The load job's filter keeps the reports/ puts; the event wakes the job, which reads what it has not handled from its durable cursor in the event log, then publishes ReportLoaded, which wakes the notice job.](/diagrams/abi-job-chain.svg)

[Chain jobs with events](/capabilities/build/orchestrations/chain-jobs-with-events) builds that chain step by step.

## Key concepts

- **A job is code.** It is declared with `@job(triggers=(...))` on the module class, in Python. Nexus shows jobs and starts them, but no screen, CLI command or `config.yaml` key creates one.
- **One replica per trigger.** The replicas of a module share one durable consumer per job, so each tick, event or manual start runs once, on one replica. `max_concurrency` (default 1) bounds runs at once across replicas.
- **At least once.** After a crash or a lost acknowledgement a run can start again. Write each job so a second run does no harm.
- **Events are at most once.** An event published while no replica of the job is running is not delivered. A job that must not miss one reads the event log instead: see [the wake-up pattern](/capabilities/build/orchestrations/chain-jobs-with-events#wake-up-then-read-the-log).
- **Run records.** Every run is recorded with its status, attempts, payload, logs and result, and shown in Nexus under System › Jobs. See [Monitor runs](/capabilities/build/pipelines/monitor-runs).

The full reference (options, `JobContext`, retries, retention, engine jobs and the move from Dagster) is on [Module jobs](/capabilities/build/modules/module-jobs).
