Triggers and Datasets
Run workflows when upstream workflows finish or datasets update, with an optional cron deadline.
Cron schedules run workflows at a fixed time and hope the input data arrived first. Triggers invert that: a workflow runs because something happened — an upstream workflow succeeded, or the datasets it reads were updated. For pipelines where late input data is the dominant failure mode (day-ahead forecasting being the canonical case), this removes the cron race entirely.
A workflow has at most one trigger, set the same way as a schedule: as a decorator argument that deploys with the workflow version.
Datasets also carry the platform's data-reliability features — output contracts and freshness SLAs — covered in Data Quality.
Chain Workflows with rebase.OnWorkflow
Run a workflow whenever another workflow's run reaches a terminal state:
import rebase as rb
project = rb.project("energy")
@project.workflow()
def ingest_prices():
...
@project.workflow(trigger=rb.OnWorkflow("energy/ingest-prices", on="success"))
def price_forecast():
...Every time ingest-prices finishes successfully, the platform enqueues a run of price_forecast. The source is referenced by name ("project/workflow", or just "workflow" for the same project) and must already be deployed.
| Option | Default | Description |
|---|---|---|
on | "success" | When to fire: "success", "failure", or "completion" (either). on="failure" is useful for cleanup or fallback workflows. |
active | True | Deploy with active=False to register the trigger paused. |
Like scheduled workflows, triggered workflows need a default for every parameter — there is no caller to supply arguments.
Datasets
A dataset is a named pointer to data that lives elsewhere (a warehouse table, a bucket prefix, a vendor feed) — the platform stores only the name and a watermark, never the data. Datasets connect producers and consumers without coupling them:
prices = rb.Dataset.from_name("nordpool/prices")
@project.workflow(schedule=rb.Cron("*/10 * * * *"))
def ingest_prices():
df = fetch_prices()
src.write(df, "raw.prices")
prices.mark_updated(watermark=str(df["loaded_at"].max()))mark_updated works out of the box inside deployed workflows and functions — the platform injects run-scoped credentials. The optional watermark is any JSON value (typically a timestamp) recording how far the data now reaches; consumers receive it for incremental reads.
Warehouse writes can signal automatically:
src = rb.sources.snowflake(connection="acme-prod")
src.write(df, "raw.prices", dataset="nordpool/prices") # signals on successful writeAnything with an API key can signal a dataset — including systems outside Rebase:
rebase dataset signal nordpool/prices --watermark '"2026-07-11T06:00:00Z"'Datasets referenced by a trigger are created automatically at deploy; rebase dataset create/list/get manage them explicitly.
React to Data with rebase.OnUpdate
Run a workflow when the datasets it depends on have new data:
prices = rb.Dataset.from_name("nordpool/prices")
weather = rb.Dataset.from_name("weather/ecmwf")
@project.workflow(
trigger=rb.OnUpdate(
[prices, weather],
require="all", # wait for every dataset (or "any")
at_most_every="15m", # rate-limit rapid updates
),
)
def price_forecast(ctx: rb.TriggerContext = None):
df = src.read(f"SELECT * FROM prices WHERE loaded_at > '{ctx.since['nordpool/prices']}'")
...With require="all" the platform accumulates updates: the workflow fires once both datasets have updated since its last triggered run, then the cycle resets. require="any" fires on every update (subject to at_most_every).
The trigger context
A workflow that declares a parameter named ctx receives a TriggerContext — ctx is reserved and never appears in the workflow's regular parameters:
| Field | Description |
|---|---|
reason | Why this run fired: "datasets_ready", "deadline", "workflow_succeeded", "workflow_failed" — or "api" for manual/scheduled runs. |
since | Per-dataset watermark consumed by the previous triggered run — the natural lower bound for incremental reads. |
latest | Per-dataset watermark and update time that fired this run. |
missing | Datasets that had not updated when a deadline fired. |
source_workflow, source_run_id | For OnWorkflow triggers: which run caused this one. |
The same workflow runs unchanged from the CLI, API, or a schedule — ctx then carries reason="api" with empty watermarks. During a replay, ctx.is_replay is True and ctx.replay describes the original run — use it to guard side effects that should not re-execute.
Deadlines: Gate-Closure Semantics
Pure reactive triggering has a gap for markets with hard submission deadlines: if an input never arrives, the workflow never runs. deadline guarantees one run per deadline period — early when everything is ready, at the deadline with whatever is available otherwise:
@project.workflow(
trigger=rb.OnUpdate(
[prices, weather],
require="all",
deadline=rb.Cron("0 9 * * *", timezone="Europe/Stockholm"),
),
)
def da_forecast(ctx: rb.TriggerContext = None):
if ctx.missing:
log.warning("running degraded, missing: %s", ctx.missing)
...If both datasets update at 07:30, the workflow fires then — and does not fire again at 09:00. If only prices arrived by 09:00, the workflow fires at the deadline with ctx.reason == "deadline" and ctx.missing == ["weather/ecmwf"], so it can run degraded (e.g. fall back to the previous weather run) instead of not running at all.
Manage Triggers from the CLI
Triggers can be inspected and changed without redeploying the workflow source:
rebase workflow trigger list # all workflows with triggers
rebase workflow trigger show forecast --project energy # config, per-source state, last fired, next deadline
rebase workflow trigger set forecast --project energy --on-workflow energy/ingest
rebase workflow trigger set forecast --project energy \
--on-update nordpool/prices --on-update weather/ecmwf \
--require all --at-most-every 15m --deadline-cron "0 9 * * *"
rebase workflow trigger pause forecast --project energy # stop firing, keep state
rebase workflow trigger resume forecast --project energy
rebase workflow trigger clear forecast --project energy # remove the triggerTrigger changes create (or reuse) a workflow version, exactly like schedule changes, and follow the same GitOps environment policies.
Inspect Triggered Runs
Triggered executions are ordinary platform runs. They carry trigger_source: "trigger" (vs "schedule" and "api"):
rebase run list --target-type workflow
rebase run get <run-id>Delivery is guaranteed by a platform sweeper: if a firing cannot be dispatched immediately, it is retried within about a minute. Failed triggered runs feed the workspace failure webhook like any other run — and can themselves fire on="failure" triggers.
Triggers vs. Schedules
Schedules and triggers can coexist on one workflow (e.g. a reactive trigger plus a nightly full rebuild). Rules of thumb:
- Upstream data lands in Rebase via another workflow →
OnWorkflow. - Upstream data lands outside a single workflow, or comes from several producers → datasets +
OnUpdate. - Hard market deadline →
OnUpdatewithdeadline, not a bare cron. - Genuinely time-based work (reports, cleanups) →
rb.Cron.

