> ## 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.

# App events

> Run workflows when connected apps emit events.

A common goal is "run this when something happens in another app," such as a new GitHub issue, a paid Stripe invoice, or a Linear status change. Keystroke doesn't have a separate app-event trigger type; instead you react to app events with the [trigger sources](/docs/learn/triggers/overview) you already have, pointed at the app. This page covers the patterns.

<Note>
  There's no `defineAppEventSource`. The three sources ([webhook](/docs/learn/triggers/webhooks), [schedule](/docs/learn/triggers/schedules), and [poll](/docs/learn/triggers/polling)) cover app events. Pick the one that matches how the app delivers events.
</Note>

## When the app sends webhooks

Most modern apps can push events to a URL. This is the best option when it's available: point the app's webhook at a [webhook trigger](/docs/learn/triggers/webhooks).

1. Define a `defineWebhookSource` with a `payload` schema for the app's event.
2. Get the URL with `keystroke triggers url <endpoint>` and register it in the app's settings.
3. Put event-type constraints (literals, enums) in the `payload` schema so only the events you care about fire.

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

export default defineWebhookSource({
  slug: "stripe-invoice-paid",
  name: "Stripe invoice paid",
  description: "Fires when Stripe sends an invoice.paid event.",
  endpoint: "stripe",
  payload: z.object({
    type: z.literal("invoice.paid"),
    data: z.object({ id: z.string() }),
  }),
}).attach({
  workflow,
  transform: (payload) => ({ invoiceId: payload.data.id }),
});
```

Providers that send every event type to one URL fit the [shared endpoint](/docs/learn/triggers/webhooks#shared-endpoints) pattern: many triggers on a single `endpoint`, each with its own `payload` for its event.

## When the app doesn't push events

If an app has no webhooks (or you'd rather pull), use a [poll trigger](/docs/learn/triggers/polling). Its `run()` checks the app's API on a schedule and starts a run only when there's something new:

```ts src/triggers/new-issues.ts theme={null}
import { definePollSource } from "@keystrokehq/keystroke/trigger";
import workflow from "../workflows/handle-issue";

export default definePollSource({
  slug: "new-issues",
  name: "New issues",
  description: "Polls for open issues every ten minutes.",
  schedule: "*/10 * * * *",
  run: async () => fetchOpenIssues(),
  filter: (result) => result.issues.length > 0,
}).attach({ workflow });
```

When the source you're polling needs credentials, resolve them through an [action](/docs/learn/actions/overview) that you call from `run()`, or read them from your project's [credentials](/docs/learn/credentials/overview).

## Chat apps: the Slack gateway

Conversations are different from events. To let people talk to an [agent](/docs/learn/agents/overview) from Slack (mention it in a channel or DM it), connect the **Keystroke Slack App** (`keystroke connect slack --kind keystroke`; catalog slug `slackbot`) and use the Slack **gateway** rather than a trigger. The gateway delivers inbound messages to an agent and posts replies back as the bot. See [external channels](/docs/learn/agents/external-channels).

## Finding integrations

Keystroke ships 1,000+ [integrations](/docs/integrations). Each one lists the actions it provides (and whether it exposes triggers), which you can use inside a poll's `run()` or as steps and tools once connected. Browse the catalog to see what's available for the app you want to react to, then wire it up with a webhook or poll as above.

## Next steps

<CardGroup cols={2}>
  <Card title="Webhooks" href="/docs/learn/triggers/webhooks">
    Receive events the app pushes to a URL.
  </Card>

  <Card title="Polling" href="/docs/learn/triggers/polling">
    Pull events from an app on a schedule.
  </Card>

  <Card title="External channels" href="/docs/learn/agents/external-channels">
    Connect agents to Slack and other chat surfaces.
  </Card>

  <Card title="Integrations" href="/docs/integrations">
    Browse 1,000+ built-in integrations.
  </Card>
</CardGroup>
