Introducing Triggers
Deck now supports triggers: run a task on a schedule, fanned out across every credential you have connected.
The problem triggers solve
Say you fetch a monthly bill for 10,000 connected utility accounts. Without triggers, you build the orchestration yourself: a daily cron that queries your active credentials, fans out a task run to each one, skips the accounts already billed this month, and sidelines the ones with expired passwords. That’s real infrastructure to build and maintain just to keep a recurring job running.
A trigger moves all of that into Deck. Declare “run this task every day at 6am against every credential on this source,” and Deck handles the scheduling, fan-out, skip logic, and observability.
How it works
A trigger ties a schedule to a task and the credentials to run it against. Each time the schedule fires, Deck creates one task run per credential in scope.
A triggered run is identical to one you create with POST /v2/tasks/{task_id}/run: same lifecycle, statuses, artifacts, and events. The only difference is the initiator, recorded as a trigger_id on the run. A trigger targets one task, fixed at creation; to run a different task, create a new trigger.
Manage triggers through the API, or from the task’s detail page in the Console, where each task shows its triggers, their enrolled credentials, and recent fires.
Example
Fetch the latest bill for every connected utility account, every day at 6am Eastern:
POST /v2/triggers
{
"name": "Daily utility bills",
"condition": {
"type": "schedule",
"cron": "0 6 * * *",
"timezone": "America/New_York"
},
"task_id": "task_a1b2c3...",
"credential_filter": {
"source_ids": ["src_utility..."],
"status": ["unverified", "verified"]
},
"input": {},
"skip_if": {
"last_run": {
"result": "success",
"within_seconds": 2160000
}
},
"concurrency_max": 50
}
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, and creates a task run for each of the rest.
Schedules
A trigger fires on a cron schedule, evaluated in the timezone you specify. condition takes a standard 5-field cron expression plus an IANA timezone name, so day boundaries and DST shifts follow local time rather than UTC.
An invalid cron expression or timezone is rejected at create time. 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.
An explicit list (credential_ids) is static: exactly the credentials you name, one or many, until you change it with a PATCH. Credentials in the list that are invalid or deleted when the trigger fires are skipped automatically, so you don’t need to prune the list yourself.
A filter (credential_filter) is a scope resolved each time the trigger fires: credentials on the listed source_ids with the listed status. Enrollment is fully dynamic. Connect a 10,001st user and that credential gets runs on the next fire, no registration step required.
Either way, a credential that goes invalid drops out of a filter on its own, or is skipped in an explicit list, and resumes automatically once the user re-authenticates. The trigger keeps running for everyone else.
Skip conditions
skip_if is evaluated per credential before a run is created. If the credential’s most recent run of the task already succeeded within a time window, it’s skipped this time and no run is created.
A bill fetched today doesn’t need re-fetching tomorrow, so a daily schedule with a 25-day success window does a handful of real runs per credential per cycle instead of thirty. At scale, that difference matters: 10,000 credentials on a daily schedule is roughly 300,000 runs a month without skipping; with a 25-day success window, it’s closer to 50,000. Pick a window slightly shorter than the real cadence of the data you’re fetching.
Skip logic depends on how the task defines success. A task built for scheduling should treat “did the work” as success and “nothing new to fetch” as unknown, not success, so checking naturally resumes in time for the next cycle.
Inputs under fan-out
A trigger has a single input, shared by every run it creates. There’s no per-credential input; 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.
If each user’s run genuinely needs a different value from your system, a trigger can’t supply it: create those runs individually with POST /v2/tasks/{task_id}/run.
Concurrency and queueing
Triggered runs don’t all execute at the same instant. 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.
Scheduled runs also draw on their own capacity pool at the organization level, 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.
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 already executing are left alone. 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. What happens next depends on the error:
- 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 credential is marked invalid and leaves the fan-out on its own, resuming once the user re-authenticates. - Configuration (a deleted task, input that no longer validates): the fire itself fails, surfaced as a
trigger_run.failedevent rather than failing silently.
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. Once the configuration is fixed, reactivate it from the Console or with PATCH { "status": "active" }.
Events
Triggers emit into Deck’s existing events system on two channels.
Lifecycle events track the configuration object: trigger.created, trigger.updated, trigger.deleted, trigger.activated, and trigger.deactivated.
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: trigger_run.completed carries the count of runs created and credentials skipped, and trigger_run.failed fires when a fire fails before creating any runs.
{
"id": "evt_a1b2c3d4",
"object": "event",
"type": "trigger_run.completed",
"data": {
"id": "tgrn_a1b2c3d4",
"object": "trigger_run",
"trigger_id": "trg_a1b2c3...",
"status": "completed",
"runs_created": 412,
"skipped_count": 9588,
"created_at": "2026-06-23T06:00:02Z"
},
"created_at": "2026-06-23T06:00:02Z"
}
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.
Managing triggers
Pause and resume by setting status to inactive or active; there are no separate endpoints. Updates take any subset of fields, and omitted fields are left unchanged. A trigger’s target task can’t be changed: to point at a different task, create a new trigger.
To see which credentials are currently in scope, use GET /v2/triggers/{trigger_id}/credentials or the Console’s enrollment view. To audit a trigger’s history, filter task runs by trigger_id:
GET /v2/task-runs?trigger_id=trg_a1b2c3...&outcome=failure
When to use triggers
If you’re running a cron against the Deck API, iterating your own credential list, and tracking which users already ran this month, that’s a trigger. Define it once and let Deck run it. If you need a task to run right now for one user, call the task run endpoint directly.