> ## Documentation Index
> Fetch the complete documentation index at: https://keystroke.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Connect cron, webhook, and poll sources.

A trigger is how a [workflow](/docs/learn/workflows/overview) or [agent](/docs/learn/agents/overview) runs automatically. It binds a **source** (a schedule, an inbound webhook, or a poll) to a target, so every matching event starts a run. Triggers hold no business logic: just the schedule, endpoint, validation, and filters. The work lives in the workflow or agent they attach to.

Use a schedule when the workflow should run **every** tick unconditionally, a webhook when another system can push an event to Keystroke, and a poll when Keystroke should check an external system and run **only when a condition is met** (a filtered poll tick creates no run).

## Example requests

Ask your coding agent when an automation should start. It can wire up the source, target, filters, and payload shape.

> "Build an agent that sends me a morning brief every weekday at 9am in Slack."

> "When a Stripe payment succeeds, update the customer record in our CRM and start the Zendesk onboarding workflow."

> "Check our vendor API every hour, and run the pending invoice workflow when a new invoice is marked as 'ready to process'."

## Source plus attach

Triggers live in your project code under `src/triggers/`. Each file defines a source, then chains `.attach()` to bind it to a workflow or agent, and **default-exports** the result.

```ts src/triggers/signup.ts theme={null}
import { defineWebhookSource } from "@keystrokehq/keystroke/trigger";
import { z } from "zod";
import workflow from "../workflows/signup-pipeline";

export default defineWebhookSource({
  slug: "signup",
  name: "Signup",
  description: "Fires when a new signup is posted.",
  endpoint: "signup",
  payload: z.object({ name: z.string(), email: z.string().email() }),
}).attach({ workflow });
```

The runtime discovers the file when you run or deploy the project. Only the default export is discovered — a single `.attach()`, a chain of `.attach()` calls, or an array of attachments (see [fan out to multiple targets](/docs/learn/triggers/advanced-triggers#fan-out-to-multiple-targets)).

## The three sources

There are exactly three trigger source types. Each takes a `slug` (the stable trigger slug), a `name`, and a `description`, plus options specific to how it fires.

| Source       | Define with                                       | Fires when                                                                                   |
| ------------ | ------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| **Schedule** | [`defineCronSource`](/docs/learn/triggers/schedules)   | A cron schedule comes due — every tick dispatches all enabled attachments (no filters)       |
| **Webhook**  | [`defineWebhookSource`](/docs/learn/triggers/webhooks) | A request `POST`s to the trigger's endpoint and matches its `payload` schema                 |
| **Poll**     | [`definePollSource`](/docs/learn/triggers/polling)     | A scheduled `run()` returns a payload that passes its filters — filtered ticks create no run |

To react to events from third-party apps (Stripe, GitHub, Linear, and so on), you point one of these sources at the app, usually a webhook and sometimes a poll. See [app events](/docs/learn/triggers/app-events).

## Attach to a workflow or an agent

`.attach()` binds the source to one of two targets:

```ts theme={null}
// Workflow target: the payload becomes the workflow input
.attach({ workflow, transform: (payload) => ({ /* … */ }) })

// Agent target: the payload becomes a prompt
.attach({ agent, prompt: (payload) => `Handle ${payload.id}` })
```

A workflow target accepts an optional `transform` to shape the payload into the workflow's input. An agent target takes a `prompt` (a string, or a function of the payload) instead. See [advanced triggers](/docs/learn/triggers/advanced-triggers) for both in depth.

## Attachment id

Each attachment has an id of the form `{sourceSlug}:{targetSlug}`, the source `slug` joined to the workflow or agent `slug`:

```
signup:signup-pipeline      # webhook → workflow
morning-check:support       # cron → agent
```

You use this id to inspect trigger-driven runs in [run history](/docs/learn/logs/overview) and from the CLI.

## Match on the source, transform on the attachment

Keep the two concerns separate:

* **Match** decides *whether* to run, and is part of the **source**: a webhook's `payload` Zod schema, or a poll's `filter` predicates.
* **`transform`** decides *what input the run receives*, and is part of the **attachment** (workflow targets only).

This split keeps "should this run?" next to the source definition and "what does the run get?" next to the binding. See [advanced triggers](/docs/learn/triggers/advanced-triggers).

## Disable individual attachments

Each attachment can be paused without removing it from your project code. Disabled attachments stay visible in the dashboard and API, but they do not match webhook ingress, receive cron ticks, or receive poll results.

* Disable one workflow or agent binding while leaving sibling attachments on the same source active.
* Re-enable the attachment later without redeploying.
* The disabled state survives redeploys when the attachment identity is unchanged — a later deploy does not re-enable a paused attachment.
* Attachment identity is `{sourceSlug}:{targetSlug}`. If you repoint a trigger to a different workflow or agent (or rename the target), that creates a **new** attachment which starts enabled. Re-disable it after deploy if it should stay paused.
* When every attachment on a scheduled source is disabled, its schedule stops firing until at least one attachment is enabled again.

**Deploy timing** — webhooks are passive and safe to deploy any time. A poll fires immediately on deploy; a cron waits for its next scheduled slot, then runs unattended. Until the workflow is verified, disable the attachment rather than removing it from code.

Use the trigger detail panel in the web app, the platform API (`PATCH /api/triggers/:triggerId/attachments/:attachmentId`), or the CLI:

```bash theme={null}
keystroke triggers disable <trigger-slug>                     # pause every attachment
keystroke triggers disable <trigger-slug> --workflow <slug>   # pause one workflow attachment
keystroke triggers disable <trigger-slug> --agent <slug>      # pause one agent attachment
keystroke triggers enable <trigger-slug>                      # resume
```

Address the trigger by id or slug. To pause a single attachment on a multi-target trigger, narrow with `--workflow`/`--agent` — you never need the attachment id.

## Inspect trigger runs

Triggers are typically operated against a deployed (cloud) project. List them, print a webhook URL, invoke on demand, and audit runs from the CLI:

```bash theme={null}
keystroke triggers list
keystroke triggers url signup
keystroke triggers invoke signup --input '{"email":"a@b.com","name":"Ada"}'
# → { runId, triggerSlug, targets } — runId is the trigger run id, not a workflow run id
keystroke triggers runs get signup <runId>
# → includes workflowRuns[] / agentSessions[] once the job has dispatched
keystroke workflows runs get <workflowRunId>
keystroke triggers runs list signup --workflow signup-pipeline
```

`triggers invoke` works for webhook, cron, and poll triggers (platform only). Webhooks take `--input` JSON matched against the attachment schema; cron and poll take no input (cron always uses its static payload). Narrow with `--workflow` / `--agent` when a trigger has multiple attachments — otherwise every enabled attachment fires (one workflow/agent run each, asynchronously).

JSON goes to stdout; human follow-up hints go to stderr so `| jq` and agents can parse stdout cleanly.

See the [CLI reference](/docs/cli#triggers) for every trigger command and [run history](/docs/learn/logs/overview) in the web app.

## Next steps

<CardGroup cols={2}>
  <Card title="Schedules" href="/docs/learn/triggers/schedules">
    Run a workflow or agent on a cron schedule.
  </Card>

  <Card title="Webhooks" href="/docs/learn/triggers/webhooks">
    Run on inbound HTTP requests with a validated payload.
  </Card>

  <Card title="Polling" href="/docs/learn/triggers/polling">
    Periodically check a source, keep cursor state, and run when there's work.
  </Card>

  <Card title="App events" href="/docs/learn/triggers/app-events">
    React to events from connected third-party apps.
  </Card>
</CardGroup>
