---
sidebar_label: Triggers
toc_max_heading_level: 3
doc_id: c2c3d1e4-5d51-4e12-8b80-2acde06e1482
description: >-
  Everything that can start a nullplatform workflow: schedules, webhooks,
  Slack, manual runs, and platform events like deployments, scopes, and
  action items.
keywords:
  - workflows
  - triggers
  - cron
  - webhook
  - platform events
  - nullplatform
---

# Triggers

Triggers decide when a workflow runs: on a schedule, when something happens in nullplatform, when an external system calls in, or by hand. A workflow can have several, and whatever a trigger produces (a webhook body, a platform event, the values you typed in the run dialog) reaches your steps as `${{ workflow.inputs.* }}`.

## Triggers go live on activation

Triggers register when you **activate** a workflow, never when you save it. Activation provisions everything the trigger needs: the schedule starts firing, the webhook URL gets minted, the notification channel gets created. Deactivating removes all of it at once. Until you activate, a saved workflow is only a definition and has no side effects.

When a workflow has more than one trigger, say a nightly schedule plus a manual "run now", the first step they share needs `join_strategy: any` so that either trigger can reach it on its own.

## Run on a schedule

A cron trigger takes a five-field cron expression and an optional IANA timezone (UTC by default):

```yaml
- id: start_cron
  type: trigger
  plugin_type: cron
  name: "Nightly scan"
  config:
    schedule: "0 3 * * *"
    timezone: "America/Argentina/Buenos_Aires"
```

Each tick starts exactly one run and passes the scheduled time to your steps as `${{ workflow.inputs.firedAt }}`. If a run is still going when the next tick arrives, that tick is skipped instead of queuing up.

## Run on nullplatform events

Nullplatform-native triggers start workflows from things happening in your organization: action item events (created, updated, commented), checklist items being dispatched, deployment lifecycles, and scope lifecycles (create, update, delete, and more). On activation they create the notification channel and its server-side filters for you; you don't configure anything by hand. You get one channel per active alias, each pointed at that alias's own endpoint, so activating `staging` next to `live` gives you two independent subscriptions rather than one shared feed.

```yaml
- id: trigger
  type: trigger
  plugin_type: np-action-item-trigger
  name: "Action item events"
  config:
    pathPrefix: np-rightsizing-events
    mode: start
    nrn: "organization=${{ vars.NP_ORGANIZATION_ID }}"
    labelFilters:
      workflow_type: rightsizing
```

Filters keep your workflow from waking up for events it doesn't care about: by NRN, by labels, by category, by priority, or by specific actions. The label, priority, and category filters run server-side only when `actions` is limited to events on the action item itself (created, updated, resolved, and the like). If the trigger also listens to comment or suggestion events, or leaves `actions` open, the channel stays broad and the trigger applies those filters after delivery, because the event payload carries the comment or suggestion rather than the item.

:::note

The notification channel is created with the session of whoever activates the workflow. Activate with an identity that's allowed to manage notification channels.

:::

There are also failure triggers: `on-error` fires when a step or workflow fails, and `execution-failed-trigger` fires when any execution in your organization ends in failure, optionally filtered to one workflow. They're how you build recovery and alerting workflows.

## Run from a webhook or from Slack

A webhook trigger turns your workflow into an HTTP endpoint, which is how systems outside nullplatform start a run: your CI, your ticketing system, a cloud provider's event bus, an internal service that knows when something relevant happened. You declare the path and method, and activation creates the public URL. Optionally, the trigger verifies an HMAC signature on the payload and validates the body against a JSON schema before your steps see it.

The URL identifies the workflow and the alias that was activated, so every active alias is its own endpoint. `live` and `staging` are different URLs pointing at different versions, so you can point a copy of the calling system at a new version while the current one keeps serving. Two things to plan for: the URL carries an opaque token, so copy it from the active triggers list in the API or the editor instead of building it yourself, and re-activating creates a new token, so whatever calls in needs the new URL.

Bodies are capped at 1 MB. When a caller has more data than that, have it post a reference and let the workflow fetch the rest; see [limits](/docs/workflows/building-blocks/limits).

A Slack trigger starts a workflow from a mention, a message matching a pattern, a reaction, or a slash command. Redeliveries are deduplicated automatically, so a retried Slack event never starts a run twice.

## Run by hand

A manual trigger declares typed inputs, and the RUN dialog renders those inputs as a form:

```yaml
- id: start_manual
  type: trigger
  plugin_type: manual
  name: "Run scan now"
  config:
    description: "Runs the scan immediately."
    inputs:
      due_days:
        type: number
        required: false
        description: "Due date offset in days for created action items"
        placeholder: "14"
```

Your steps read the submitted values as `${{ workflow.inputs.due_days }}`. One thing to know: an input's `default` is hint text for the form, and it is not applied at runtime. For a constant that needs a real value at runtime, declare a workflow variable with `initialValue` instead.

## Next steps

- [Nodes](/docs/workflows/building-blocks/nodes): what runs after the trigger fires
- [Runs and versions](/docs/workflows/building-blocks/runs-and-versions): activation and what it provisions
- [Limits](/docs/workflows/building-blocks/limits): payload sizes, step timeouts, and how to stay under them
- [Detect and roll out AMI updates](/docs/tutorials/ami-drift): cron and manual triggers working together in a real suite
