Skip to main content
Run a task on a schedule across your connected credentials. You define a trigger once, and Deck creates and manages the runs from there. A trigger pairs a schedule with a task and a set of credentials. Each time the schedule fires, Deck creates one task run per credential in scope, skipping any that don’t need the work. Triggered runs behave exactly like runs you create with POST /v2/tasks/{task_id}/run: same lifecycle, statuses, artifacts, and events.

Example

Fetch the latest bill for every connected utility account, every day at 6am Eastern:
Every day at 6am, Deck resolves the credentials matching the filter, skips any whose latest run of this task succeeded within the past 25 days (2,160,000 seconds), and creates a task run for each of the rest.

Definition fields

The response is the full trigger object (trg_ prefix) with status: "active". Triggers fire on their schedule from the moment they’re created.

The schedule

condition defines when the trigger fires: a cron expression evaluated in a timezone.
An invalid cron expression or timezone is rejected at create time with a standard validation error. Missed occurrences are skipped, not replayed. If a trigger sits inactive across several scheduled times, reactivating it resumes at the next future occurrence; Deck never back-fills fires it missed.

Choosing credentials

Every trigger targets credentials one of two ways. Pass exactly one of credential_ids / credential_filter.

An explicit list

The set is static: exactly the credentials you list, one or many, until you change it with a PATCH. There’s no status allowlist because you’ve named them directly. Credentials in the list that are invalid or deleted when the trigger fires are skipped automatically; you don’t need to prune the list.

A filter

Deck resolves the filter each time the trigger fires and creates a run for every match, so enrollment is fully dynamic. A credential created tomorrow that matches the filter starts getting runs on the next fire, with no registration step. Both keys are lists and both are required:
  • source_ids matches credentials on any of the listed sources, or on every source with the ["*"] wildcard.
  • status is an explicit allowlist of credential statuses to fan out to, typically unverified and verified. There is no default. invalid and deleted credentials can’t run, so they never match.
A credential’s open or closed session state is independent of its status. A credential at rest still matches the filter, and the run authenticates a fresh session as needed. A filter that can never match anything (an empty status, or an empty source_ids) is rejected when you create or update the trigger with a standard validation error, rather than accepted as a trigger that fires and creates nothing.

Invalid credentials

When a credential goes invalid (a changed password, a locked account), Deck emits a credential.invalid event so you can prompt the user to re-authenticate. Under a filter, the credential drops out of the match entirely; under an explicit list, it stays listed but is skipped. Either way the trigger keeps running for every healthy credential, and once the user re-authenticates, runs resume automatically on the next fire. The trigger doesn’t track credential failures itself; a credential’s status determines whether it runs.

Skip conditions

skip_if is evaluated per credential each time the trigger fires, before a run is created. When it’s met, the credential is skipped this time and no run is created.
last_run looks at the credential’s most recent completed run of the trigger’s task, whether that run came from this trigger or from a direct API call. If its result matches within the window, the credential is skipped. result is the task run’s result field: success, failure, or unknown.

Design the task so skipping self-heals

The utility bill case works when the task’s success criteria mean “did the work”: a run that fetches a bill returns result: success, and a run that finds no new bill completes with result: unknown. After a bill is fetched, the credential is skipped for 25 days, and checking resumes once the window lapses, in time for the next bill. This comes from how the task defines success rather than from any trigger setting. If a no-bill run reported success, it would suppress the checks that should follow.

Using skip conditions to control cost

Every run executes in a real agent session. 10,000 credentials on a daily schedule is roughly 300,000 runs a month without skipping. With a 25-day success window, each credential only runs on the few checking days before its next bill lands, roughly 50,000 runs a month. Pick a window slightly shorter than the real cadence of the data.

Inputs under fan-out

A trigger has a single input, shared by every run it creates. There is no per-credential input. If each user’s run needs a different value from your system (a target date, a confirmation number), a trigger can’t supply it: create those runs from your own code with POST /v2/tasks/{task_id}/run.
The trigger’s input is validated against the task’s input_schema, the same shape as the POST /v2/tasks/{task_id}/run body. Fields the task declares as tokenized are vaulted when you create the trigger, exactly as they are at run time. Per-user differentiation comes from the credential, not the input. Each run executes in that credential’s authenticated session, so a task like “fetch the latest bill” runs with input: {} and fetches the right bill for whoever’s logged in. Design tasks you plan to schedule this way: operate from the logged-in context and discover what’s needed on the source, rather than expecting per-user parameters.

Managing triggers

Creating, listing, updating, and deleting triggers is covered endpoint by endpoint in the API reference. For day-to-day management (pausing a schedule, editing a filter, checking what’s enrolled, reviewing recent fires), the Console is usually the quicker path: each task’s detail page has a Triggers section with everything the API offers. The semantics that aren’t obvious from the endpoint docs:
  • Updates take any subset of fields, and omitted fields are left unchanged. An explicit null clears description, skip_if, or concurrency_max, and credential_ids replaces the existing list wholesale.
  • Pause and resume by setting status to inactive or active; there are no separate endpoints. An inactive trigger keeps its configuration and fires nothing, and reactivating resumes at the next scheduled occurrence.
  • A trigger’s target can’t be changed. To point at a different task, create a new trigger.
To see which credentials are currently in scope (the set that gets runs on the next fire), use GET /v2/triggers/{trigger_id}/credentials or the Console’s enrollment view. In the other direction, a credential’s GET response lists the triggers targeting it.

Tracing runs to a trigger

Triggered runs are normal task runs with one addition: a trigger_id field, always present and null unless the run was created by a trigger. Filter runs by it to inspect a single fire or audit a trigger’s history, combined with the existing task run filters:

Queueing and concurrency

Triggered runs don’t all execute at the same instant. Each run needs an agent session, and runs are admitted as capacity frees up.
  • Per trigger. concurrency_max caps simultaneous runs from one trigger. A fan-out matching 2,000 credentials with concurrency_max: 50 runs 50 at a time, with the rest queued.
  • Per organization. Scheduled runs draw on their own capacity pool, separate from on-demand runs, and on-demand runs are admitted first. A large overnight fan-out never delays a user-facing POST /v2/tasks/{task_id}/run.
You don’t manage session_limit_exceeded for scheduled work. Deck queues internally and admits runs as slots free up.

Overlapping fires

If a trigger fires again while the previous fire’s runs are still draining, Deck coalesces: the newer fire supersedes any still-queued run for the same credential, so the freshest scheduled work wins. Runs that are already executing are left alone.

Exactly-once per occurrence

Each fire is keyed by the trigger and its scheduled time, so a duplicate delivery of the same occurrence never creates a second batch of runs.

Failure handling

A failed triggered run is a normal task run: queryable by trigger_id and status, carrying the standard errors array. What happens next depends on the error class:
  • Transient (source_not_available, blocked, timeout): nothing special. The credential simply runs again on the next fire.
  • Credential (auth_invalid, password_reset_required, account_locked): the agent marks the credential invalid and it leaves the fan-out on its own, resuming once the user re-authenticates. See Invalid credentials.
  • Configuration (a deleted task, input that no longer validates): these fail the fire itself, surfaced as a trigger_run.failed event rather than failing silently.

Auto-deactivate

If a trigger’s fires keep failing (default threshold: 5), it transitions to inactive and emits trigger.deactivated with the reason, so a misconfigured trigger doesn’t keep failing on schedule indefinitely. This deactivates the whole trigger, never individual credentials. A deactivated trigger shows on its task’s detail page in the Console along with the failed fires that got it there. Once the configuration is fixed, reactivate it from the same page or with PATCH { "status": "active" }.

Events

Triggers emit into the existing events system on two channels. Lifecycle events track the configuration object: Trigger run events track each fire. A trigger run (tgrn_ prefix) is one scheduled execution: the fan-out across credentials that creates the underlying task runs.
The task runs a fire creates emit their own standard task_run.* events, tagged with trigger_id, so existing handlers work for scheduled runs without changes.