@trigger.dev/sdk 4.8.0-webhooks.20261020092920 → 4.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/commonjs/v3/ai.d.ts +2 -1
  2. package/dist/commonjs/v3/ai.js +18 -1
  3. package/dist/commonjs/v3/ai.js.map +1 -1
  4. package/dist/commonjs/v3/webhooks.d.ts +11 -3
  5. package/dist/commonjs/v3/webhooks.js +17 -1
  6. package/dist/commonjs/v3/webhooks.js.map +1 -1
  7. package/dist/commonjs/version.js +1 -1
  8. package/dist/esm/v3/ai.d.ts +2 -1
  9. package/dist/esm/v3/ai.js +18 -1
  10. package/dist/esm/v3/ai.js.map +1 -1
  11. package/dist/esm/v3/webhooks.d.ts +11 -3
  12. package/dist/esm/v3/webhooks.js +17 -1
  13. package/dist/esm/v3/webhooks.js.map +1 -1
  14. package/dist/esm/version.js +1 -1
  15. package/docs/ai/prompts.mdx +9 -6
  16. package/docs/ai-chat/backend.mdx +10 -7
  17. package/docs/ai-chat/chat-local.mdx +4 -4
  18. package/docs/ai-chat/client-protocol.mdx +6 -3
  19. package/docs/ai-chat/fast-starts.mdx +1 -1
  20. package/docs/ai-chat/overview.mdx +4 -3
  21. package/docs/ai-chat/patterns/database-persistence.mdx +3 -3
  22. package/docs/ai-chat/patterns/native-compaction.mdx +6 -6
  23. package/docs/ai-chat/patterns/redacting-telemetry.mdx +5 -5
  24. package/docs/ai-chat/patterns/sub-agents.mdx +1 -1
  25. package/docs/ai-chat/patterns/version-upgrades.mdx +3 -3
  26. package/docs/ai-chat/pending-messages.mdx +1 -1
  27. package/docs/ai-chat/reference.mdx +4 -4
  28. package/docs/ai-chat/side-channels.mdx +2 -2
  29. package/docs/ai-chat/types.mdx +5 -5
  30. package/docs/apikeys.mdx +1 -1
  31. package/docs/database-connections.mdx +2 -2
  32. package/docs/deploy-environment-variables.mdx +19 -0
  33. package/docs/introduction.mdx +104 -24
  34. package/docs/mcp-tools.mdx +5 -4
  35. package/docs/self-hosting/env/supervisor.mdx +1 -0
  36. package/docs/self-hosting/env/webapp.mdx +24 -0
  37. package/docs/self-hosting/overview.mdx +2 -0
  38. package/docs/webhooks/channels.mdx +29 -7
  39. package/docs/webhooks/connect.mdx +14 -7
  40. package/docs/webhooks/deliveries.mdx +8 -5
  41. package/docs/webhooks/filters.mdx +8 -4
  42. package/docs/webhooks/human-in-the-loop.mdx +9 -6
  43. package/docs/webhooks/linear.mdx +2 -5
  44. package/docs/webhooks/overview.mdx +16 -4
  45. package/docs/webhooks/session-routing.mdx +8 -5
  46. package/docs/webhooks/slack.mdx +44 -19
  47. package/docs/webhooks/sources.mdx +36 -7
  48. package/docs/webhooks/waiters.mdx +0 -4
  49. package/package.json +2 -2
package/docs/apikeys.mdx CHANGED
@@ -83,7 +83,7 @@ Access presets define what a key can do. On the Free and Hobby plans only the **
83
83
  | **Task operator** | Trigger all or selected tasks and inspect or operate on their runs |
84
84
  | **Observer** | Read runs, tasks, batches, logs, traces, queues, errors, and webhooks |
85
85
  | **Operator** | Observe and operate on runs, queues, errors, and webhooks, and trigger tasks |
86
- | **Webhooks only** | Read and write [webhooks](/webhooks/overview), which are in private beta. Replaying deliveries that trigger tasks and creating webhook waiters require task or waitpoint scopes |
86
+ | **Webhooks only** | Read and write [webhooks](/webhooks/overview). Replaying deliveries that trigger tasks and creating webhook waiters require task or waitpoint scopes |
87
87
  | **Waitpoints only** | Create, list, and retrieve [waitpoint tokens](/wait-for-token). No task or run access |
88
88
  | **Deploy only** | Deploy versions, sync environment variables, and manage Preview branches |
89
89
  | **Variables only** | Read and write environment variables in this environment |
@@ -176,13 +176,13 @@ A chat agent runs one long-lived worker per conversation and suspends between me
176
176
 
177
177
  ```ts /trigger/chat.ts
178
178
  import { chat } from "@trigger.dev/sdk/ai";
179
- import { streamText } from "ai";
179
+
180
180
  import { openai } from "@ai-sdk/openai";
181
181
  import { prisma } from "@/lib/db";
182
182
 
183
183
  export const myChat = chat.agent({
184
184
  id: "my-chat",
185
- run: async ({ messages, clientData, signal }) => {
185
+ run: async ({ messages, clientData, signal, streamText }) => {
186
186
  const user = await prisma.user.findUnique({ where: { id: clientData.userId } });
187
187
  // The connection is back in the pool before the model stream starts.
188
188
  return streamText({
@@ -192,6 +192,25 @@ The context object provides:
192
192
 
193
193
  For more information about the context object, see the [Context documentation](/context).
194
194
 
195
+ #### Linking to a run in the dashboard
196
+
197
+ Trigger.dev sets `TRIGGER_DASHBOARD_URL` on every run, in development and deployed environments. Its value is the dashboard's address without a trailing slash, so it also points at your own instance when you self-host. Append `/runs/<runId>` to link to a run:
198
+
199
+ ```ts
200
+ import { task } from "@trigger.dev/sdk";
201
+
202
+ export const reportTask = task({
203
+ id: "report-task",
204
+ run: async (payload, { ctx }) => {
205
+ const runUrl = `${process.env.TRIGGER_DASHBOARD_URL}/runs/${ctx.run.id}`;
206
+
207
+ console.log(`View this run: ${runUrl}`);
208
+ },
209
+ });
210
+ ```
211
+
212
+ The `/runs/<runId>` link checks that the viewer is a member of the run's organization, then redirects to the full run page. If you set your own `TRIGGER_DASHBOARD_URL` environment variable on the project, your value is used instead.
213
+
195
214
  ### Sync env vars from another service
196
215
 
197
216
  There are two ways to pull secrets from another service into Trigger.dev: a native **Secret Sync** (currently [Infisical](https://infisical.com)), or the `syncEnvVars` build extension for any other service.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: "Trigger.dev docs"
3
3
  sidebarTitle: "Introduction"
4
- description: "Build and run durable AI agents and workflows in TypeScript. Long-running tasks with retries, queues, observability, and elastic scaling. Open source and self-hostable."
4
+ description: "Build and run durable AI agents and workflows in TypeScript. Long-running tasks with retries, queues, webhooks, observability, and elastic scaling. Open source and self-hostable."
5
5
  mode: "center"
6
6
  ---
7
7
 
@@ -11,8 +11,8 @@ import AgentSetupPrompt from "/snippets/agent-setup-prompt.mdx";
11
11
  <Card title="Quick start" icon="rocket" href="/quick-start" color="#A8FF53">
12
12
  Go from zero to your first task in 3 minutes.
13
13
  </Card>
14
- <Card title="Build AI agents" icon="robot" href="/guides/ai-agents/overview" color="#a855f7">
15
- Agent patterns, worked examples, and the tools to run them in production.
14
+ <Card title="Build AI agents" icon="robot" href="/ai-chat/overview" color="#a855f7">
15
+ Durable, multi-turn agents with `chat.agent`, from your frontend to Slack and Linear.
16
16
  </Card>
17
17
  <Card title="AI coding assistants" icon="wand-magic-sparkles" href="/building-with-ai" color="#3B82F6">
18
18
  Let Claude Code, Cursor and Windsurf write correct Trigger.dev code with the MCP server, Skills and rules.
@@ -28,48 +28,124 @@ import AgentSetupPrompt from "/snippets/agent-setup-prompt.mdx";
28
28
 
29
29
  ## What is Trigger.dev?
30
30
 
31
- Trigger.dev is the open source platform for building and running durable AI agents and workflows in TypeScript. Run them for hours, configure automatic retries, control concurrency, and manage queues. There's no execution timeout, and no queue or worker infrastructure for you to build.
31
+ Trigger.dev is the open source platform for building and running durable AI agents and workflows in TypeScript. You write tasks and agents in your existing codebase, and they run on Trigger.dev with retries, queues, waits, full tracing, and elastic scaling. There's no execution timeout, and no queue or worker infrastructure for you to build.
32
32
 
33
- You write agents and tasks in your existing codebase using our CLI and SDK, and get durable execution, queues, waits, full tracing, and elastic scaling without building any of it yourself.
33
+ Trigger.dev's core is Apache-2.0 licensed. [Self-host it](/self-hosting/overview) on your own infrastructure, or let [Trigger.dev Cloud](https://cloud.trigger.dev) run it for you.
34
34
 
35
- Trigger.dev's core is Apache-2.0 licensed, a genuine OSI license. [Self-host it](/self-hosting/overview) on your own infrastructure, or let [Trigger.dev Cloud](https://cloud.trigger.dev) run it for you.
35
+ ## Build agents with `chat.agent`
36
36
 
37
- ## Core concepts
37
+ A `chat.agent` runs each conversation as one durable task. It wakes when a message arrives and idles between turns, and its state survives page refreshes, deploys, and crashes. Turn lifecycle, resuming a stream mid-reply, and approvals are handled for you, and your frontend connects with the AI SDK's `useChat`, with no API routes to write.
38
+
39
+ ```ts trigger/chat.ts
40
+ import { chat } from "@trigger.dev/sdk/ai";
41
+ import { anthropic } from "@ai-sdk/anthropic";
42
+
43
+ export const supportAgent = chat.agent({
44
+ id: "support-agent",
45
+ run: async ({ messages, signal, streamText }) =>
46
+ streamText({
47
+ model: anthropic("claude-sonnet-4-5"),
48
+ messages,
49
+ abortSignal: signal,
50
+ }),
51
+ });
52
+ ```
53
+
54
+ <CardGroup cols={3}>
55
+ <Card title="Agent quick start" icon="rocket" href="/ai-chat/quick-start">
56
+ Build a chat agent and its frontend end to end.
57
+ </Card>
58
+ <Card title="Sessions" icon="layer-group" href="/ai-chat/sessions">
59
+ Each conversation's durable state and two-way stream, across all its runs.
60
+ </Card>
61
+ <Card title="Tools" icon="screwdriver-wrench" href="/ai-chat/tools">
62
+ Declare tools on your agent and get them back typed in `run()`.
63
+ </Card>
64
+ <Card title="Human in the loop" icon="hand" href="/ai-chat/patterns/human-in-the-loop">
65
+ Pause a tool call for approval and resume when a person decides.
66
+ </Card>
67
+ <Card title="Slack agents" icon="slack" href="/webhooks/slack">
68
+ Turn an agent into a native Slack agent with `@trigger.dev/slack`.
69
+ </Card>
70
+ <Card title="Linear agents" icon="list-check" href="/webhooks/linear">
71
+ Assign Linear issues to an agent with `@trigger.dev/linear`.
72
+ </Card>
73
+ </CardGroup>
74
+
75
+ Version your [prompts](/ai/prompts) in code and change them from the dashboard, and see every model step and tool call, with tokens, cost and latency, in [AI observability](/ai/observability).
76
+
77
+ ## Run work
38
78
 
39
79
  <CardGroup cols={2}>
40
80
  <Card title="Writing tasks" icon="pen-to-square" href="/tasks/overview" color="#A8FF53">
41
- The durable functions at the core of Trigger.dev. Write them in your existing codebase, with retries and no timeouts.
81
+ The durable functions at the core of Trigger.dev, written in your existing codebase.
42
82
  </Card>
43
83
  <Card title="Triggering tasks" icon="bullseye-pointer" href="/triggering" color="#3b82f6">
44
- Trigger a task from your backend, your frontend, a schedule, or another task.
84
+ Trigger a task from your backend, your frontend, or another task.
45
85
  </Card>
46
86
  <Card title="Runs" icon="play" href="/runs" color="#28bf5c">
47
- Every execution of a task is a run, with its status, retries, and logs recorded.
87
+ Every execution of a task is a run, with its status, retries, logs and trace.
88
+ </Card>
89
+ <Card title="Realtime" icon="bolt" href="/realtime/overview" color="#F43F5E">
90
+ Stream run status and LLM output to your frontend with React hooks.
91
+ </Card>
92
+ </CardGroup>
93
+
94
+ ## React to events
95
+
96
+ <CardGroup cols={2}>
97
+ <Card title="Webhooks" icon="webhook" href="/webhooks/overview" color="#a855f7">
98
+ Hosted, verified endpoints for Stripe, GitHub and more, delivered to your tasks and agents.
99
+ </Card>
100
+ <Card title="Scheduled tasks" icon="clock" href="/tasks/scheduled" color="#f6cd46">
101
+ Run a task on a cron schedule, with timezone support.
102
+ </Card>
103
+ <Card title="Webhook waiters" icon="bell" href="/webhooks/waiters" color="#3b82f6">
104
+ Pause a run until a matching webhook arrives, like a payment confirmation.
48
105
  </Card>
49
- <Card title="Idempotency" icon="fingerprint" href="/idempotency" color="#F43F5E">
50
- Use an idempotency key so a retried or duplicated trigger runs once, not twice.
106
+ <Card title="Waits" icon="hourglass" href="/wait" color="#28bf5c">
107
+ Pause a run for a duration, until a date, or until a token is completed. A waiting run doesn't use compute.
51
108
  </Card>
52
109
  </CardGroup>
53
110
 
54
- ## Build with agents
111
+ ## Run in production
55
112
 
56
- Build durable, multi-turn agents with the [chat agent](/ai-chat/overview): one long-running task per conversation, with the tools it can call, streaming, and resumability handled for you. Add human-in-the-loop approval by pausing a run on a [waitpoint](/wait-for-token) until a person signs off, and a waiting run doesn't consume compute. Stream run status and LLM output straight to your frontend with the [Realtime API](/realtime/overview) and React hooks, and record your Vercel AI SDK calls as spans with the model, token counts, cost and latency using [AI observability](/ai/observability). Version your [prompts](/ai/prompts) in code, then edit the text or swap the model from the dashboard without redeploying.
113
+ <CardGroup cols={2}>
114
+ <Card title="Queues & concurrency" icon="layer-group" href="/queues" color="#3b82f6">
115
+ Control how many runs execute at once, globally or per tenant.
116
+ </Card>
117
+ <Card title="Machines" icon="microchip" href="/machines" color="#a855f7">
118
+ Choose the CPU and memory each task runs on.
119
+ </Card>
120
+ <Card title="Deployment" icon="cloud-arrow-up" href="/deployment/overview" color="#28bf5c">
121
+ Atomic, versioned deploys, with preview branches and environments.
122
+ </Card>
123
+ <Card title="Errors & retries" icon="arrows-rotate" href="/errors-retrying" color="#F43F5E">
124
+ Retry failed attempts automatically, and decide what happens when a task throws.
125
+ </Card>
126
+ <Card title="Logs, traces & metrics" icon="chart-line" href="/logging" color="#f6cd46">
127
+ A full trace of every run, with your logs and spans.
128
+ </Card>
129
+ <Card title="Query & dashboards" icon="table" href="/observability/query" color="#A8FF53">
130
+ Query your runs with TRQL, and build dashboards from the results.
131
+ </Card>
132
+ </CardGroup>
57
133
 
58
- ## Scale and scheduling
134
+ ## Frequently asked questions
59
135
 
60
- Set how many runs of a task execute at once, globally or per tenant, with [concurrency](/concurrency) and [queues](/queues). Run a task on a [cron schedule](/tasks/scheduled) with timezone support, choose the CPU and memory each task runs on with [machines](/machines), and control what happens when a task throws with [errors and retries](/errors-retrying).
136
+ **Can I use Trigger.dev for background jobs, not just AI?** Yes. The same durable tasks, queues, retries, and [cron schedules](/tasks/scheduled) run any background job or workflow, with or without AI.
61
137
 
62
- ## Self-hosting
138
+ **How long can a run take?** As long as it needs. Runs have no maximum duration by default, and you can set a [`maxDuration`](/runs/max-duration) for a project, a task, or a single run to cap it.
63
139
 
64
- Trigger.dev is open source and you can self-host. Read the [overview guide](/self-hosting/overview), or how to do it with [Docker](/self-hosting/docker) or [Kubernetes](/self-hosting/kubernetes).
140
+ **Do my serverless function timeouts apply?** No. Tasks run on Trigger.dev, not inside your serverless functions, so your platform's function timeout isn't in the execution path. Your backend triggers the task and returns.
65
141
 
66
- ## Frequently asked questions
142
+ **Can I receive webhooks without running a server?** Yes. Declare a [webhook endpoint](/webhooks/overview) in your code, and Trigger.dev hosts its URL, verifies each request, and delivers the event to your tasks and agents.
67
143
 
68
- **How do I add human-in-the-loop approval to an agent?** Pause the run on a [waitpoint](/wait-for-token) until a person approves the action. A waiting run doesn't consume compute, so it can wait minutes or days.
144
+ **How do I add human-in-the-loop approval?** Pause the run on a [waitpoint](/wait-for-token) until a person approves, or use a [chat agent's approvals](/ai-chat/patterns/human-in-the-loop) for tool calls. A waiting run doesn't use compute, so it can wait minutes or days.
69
145
 
70
146
  **Does Trigger.dev support tool calling and MCP?** Yes. Agents call the tools you define, and the [MCP server](/building-with-ai) lets AI coding assistants trigger tasks, deploy, and read run traces.
71
147
 
72
- **Can I use Trigger.dev for background jobs, not just AI?** Yes. The same durable tasks, queues, retries, and [cron schedules](/tasks/scheduled) run any background job or workflow, with or without AI.
148
+ **Can I self-host?** Yes. Trigger.dev is open source. Read the [self-hosting overview](/self-hosting/overview), or set it up with [Docker](/self-hosting/docker) or [Kubernetes](/self-hosting/kubernetes).
73
149
 
74
150
  ## Getting help
75
151
 
@@ -85,7 +161,7 @@ We'd love to hear from you or give you a hand getting started. Here are some way
85
161
  Our Discord is the best place to get help with any questions about Trigger.dev.
86
162
  </Card>
87
163
  <Card
88
- title="Follow us on X (Twitter)"
164
+ title="Follow us on X"
89
165
  icon={
90
166
  <svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 0 512 512">
91
167
  <path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z" />
@@ -112,7 +188,7 @@ We'd love to hear from you or give you a hand getting started. Here are some way
112
188
  href="https://github.com/triggerdotdev/trigger.dev"
113
189
  color="#fbbf24"
114
190
  >
115
- Check us out our GitHub repo and give us a star if you like what we're doing.
191
+ Check out our GitHub repo, and give us a star if you like what we're doing.
116
192
  </Card>
117
193
  </CardGroup>
118
194
 
@@ -122,12 +198,16 @@ We'd love to hear from you or give you a hand getting started. Here are some way
122
198
 
123
199
  **Current major version:** v4 (`@trigger.dev/sdk` 4.x). Documentation under `/docs/v2` and `/docs/v3` describes superseded versions and should not be used.
124
200
 
125
- **Execution model:** tasks run on Trigger.dev infrastructure, not inside your serverless functions, so a platform function timeout is not in the execution path. A task runs until it finishes; you can cap runtime with `maxDuration` (minimum 5 seconds).
201
+ **Execution model:** tasks run on Trigger.dev infrastructure, not inside your serverless functions, so a platform function timeout is not in the execution path. Runs have no maximum duration by default; `maxDuration` caps a run's compute time for a project, a task, or a single run, and `timeout.None` removes the cap.
126
202
 
127
203
  **Deploys:** each deploy is an atomic version. A run already in progress is not affected by code shipped after it started.
128
204
 
129
205
  **Durability and retries:** retries are automatic and configurable. A failed attempt is retried according to the task's retry settings; once retries are exhausted the run ends terminally as `FAILED`, and some crashes (for example out of memory) end the run as `CRASHED` without a retry. Idempotency keys deduplicate the task triggers and waits you key, so the same key runs once instead of again.
130
206
 
207
+ **Agents:** `chat.agent` is imported from `@trigger.dev/sdk/ai`, not from `@trigger.dev/sdk`. Inside `run()`, call the `streamText` passed in the run options. It records each model step and tool call as a span by default. Slack and Linear agents use the `@trigger.dev/slack` and `@trigger.dev/linear` packages, and `slack()` requires a `startOn` filter (`appMentions()` answers @mentions).
208
+
209
+ **Webhooks:** `webhooks.endpoint.define()` declares a hosted, verified endpoint that syncs on `trigger dev` and on deploy; `webhook()` subscribes a task with a filter, and `webhooks.waiter` pauses a run until a matching event arrives. Both come from `@trigger.dev/sdk`.
210
+
131
211
  **License:** the platform core is Apache-2.0 and the `@trigger.dev/sdk` package is MIT, permissive open source, not fair-code or source-available. Some features are gated, but the open source core is not relicensed or held back.
132
212
 
133
213
  **LLM observability coverage:** only Vercel AI SDK calls (`generateText`, `streamText`, `generateObject`) are recorded as spans. Raw `fetch` calls and provider SDKs used directly are not captured.
@@ -4,8 +4,6 @@ sidebarTitle: "Tools"
4
4
  description: "Learn about how to use the tools available in the Trigger.dev MCP Server"
5
5
  ---
6
6
 
7
- import WebhooksBetaNote from "/snippets/webhooks-beta-note.mdx";
8
-
9
7
  ## Documentation and Search Tools
10
8
 
11
9
  ### search_docs
@@ -352,8 +350,6 @@ Append one record to a named side channel's `in` stream. Sends control input to
352
350
 
353
351
  ## Webhook Tools
354
352
 
355
- <WebhooksBetaNote />
356
-
357
353
  Set up and debug [hosted webhook endpoints](/webhooks/overview). Endpoint ids start with `wh_` and delivery ids with `whd_`; endpoint tools also accept an endpoint's declared id. The same operations are on the [Webhooks API](/management/webhooks/list-endpoints).
358
354
 
359
355
  ### list_webhook_endpoints
@@ -415,6 +411,10 @@ Generate a new signing secret for an endpoint and return it once, for providers
415
411
 
416
412
  **Parameters:**
417
413
  - `endpointId` (required): the endpoint's declared id (such as `payments`) or `wh_` id
414
+ - `previousSecretTtlSeconds` (optional): keep accepting the replaced secret for this many seconds (up to 604800, 7 days) while the provider switches to the new one. Omit it, or pass `0`, to replace the secret immediately, for example when it leaked. The response says when the old secret stops working.
415
+
416
+ **Example usage:**
417
+ - `"Rotate the GitHub endpoint's secret and keep the old one working for a day"`
418
418
 
419
419
  ### set_webhook_secret
420
420
 
@@ -423,6 +423,7 @@ Store a signing secret or public key the provider issued, such as Stripe's `whse
423
423
  **Parameters:**
424
424
  - `endpointId` (required): the endpoint's declared id (such as `payments`) or `wh_` id
425
425
  - `secret` (required): the secret or public key
426
+ - `previousSecretTtlSeconds` (optional): keep accepting the replaced secret for this many seconds (up to 604800, 7 days) while the provider switches to the new one. Omit it, or pass `0`, to replace the secret immediately, for example when it leaked. The response says when the old secret stops working.
426
427
 
427
428
  <Callout type="warning">
428
429
  `replay_webhook_delivery`, `generate_webhook_secret` and `set_webhook_secret` are write operations and are not available in readonly mode.
@@ -52,6 +52,7 @@ mode: "wide"
52
52
  | `KUBERNETES_IMAGE_PULL_SECRETS` | No | — | Image pull secrets (CSV). |
53
53
  | `KUBERNETES_EPHEMERAL_STORAGE_SIZE_LIMIT` | No | 10Gi | Ephemeral storage size limit. Applies to all runs. |
54
54
  | `KUBERNETES_EPHEMERAL_STORAGE_SIZE_REQUEST` | No | 2Gi | Ephemeral storage size request. Applies to all runs. |
55
+ | `KUBERNETES_POD_RESERVED_SOURCE_PORTS` | No | — | Ports runs never use as the source port of an outbound connection, so they can't clash with services on those ports. CSV of ports or ranges, e.g. `50080,50081` or `50080-50090`. Sets the pod's `net.ipv4.ip_local_reserved_ports` sysctl. |
55
56
  | **Metrics** | | | |
56
57
  | `METRICS_ENABLED` | No | true | Enable metrics. |
57
58
  | `METRICS_COLLECT_DEFAULTS` | No | true | Collect default metrics. |
@@ -192,6 +192,18 @@ mode: "wide"
192
192
  | `RUN_ENGINE_DEFAULT_MAX_TTL` | No | — | Maximum TTL for all runs (e.g. "14d"). Runs without a TTL use this as default; runs with a larger TTL are clamped. |
193
193
  | `MAXIMUM_DEV_QUEUE_SIZE` | No | — | Maximum queued runs per queue in development environments. |
194
194
  | `MAXIMUM_DEPLOYED_QUEUE_SIZE` | No | — | Maximum queued runs per queue in deployed (staging/prod) environments. |
195
+ | **Webhooks** | | | |
196
+ | `WEBHOOK_ENABLED` | No | 0 | Set to `1` to run hosted webhooks: ingest, delivery and waiters. See [Webhooks](#webhooks). |
197
+ | `WEBHOOK_REDIS_HOST` | No | localhost | A node of the Redis cluster webhooks use for their queue and waiters. It must run in cluster mode. |
198
+ | `WEBHOOK_REDIS_PORT` | No | 6379 | Port of that node. |
199
+ | `WEBHOOK_REDIS_USERNAME` | No | — | Webhooks Redis username. |
200
+ | `WEBHOOK_REDIS_PASSWORD` | No | — | Webhooks Redis password. |
201
+ | `WEBHOOK_REDIS_TLS_DISABLED` | No | `REDIS_TLS_DISABLED` | Set to `true` to connect to the webhooks Redis without TLS. |
202
+ | `WEBHOOK_INGRESS_ORIGIN` | No | `API_ORIGIN` | Public origin of the webhook URLs, for when you serve them from their own host. |
203
+ | `WEBHOOK_INGRESS_BODY_SIZE_LIMIT_MB` | No | 1 | Largest webhook request body accepted, in MB. |
204
+ | `WEBHOOK_DATABASE_URL` | No | `DATABASE_URL` | A separate PostgreSQL database for webhook endpoints and deliveries. |
205
+ | `WEBHOOK_DELIVERY_RETENTION_DAYS` | No | 30 | How many days of deliveries the dashboard and API show. It doesn't delete anything: deliveries are kept for `WEBHOOK_DELIVERY_STORAGE_DAYS`, or longer when this is higher. |
206
+ | `WEBHOOK_DELIVERY_STORAGE_DAYS` | No | 30 | How long deliveries are stored before they're deleted. Must be one of `3`, `7`, `30`, `90`, `180` or `365`; any other value stops the webapp from starting. |
195
207
  | **Misc** | | | |
196
208
  | `TRIGGER_TELEMETRY_DISABLED` | No | — | Disable telemetry. |
197
209
  | `NODE_MAX_OLD_SPACE_SIZE` | No | 8192 | Maximum memory allocation for Node.js heap in MiB (e.g. "4096" for 4GB). |
@@ -202,6 +214,18 @@ mode: "wide"
202
214
  | `ADMIN_DASHBOARD_ENABLED` | No | 1 | Set to anything other than `1` or `true` to disable the admin dashboard and user impersonation on this instance. |
203
215
  | `EVENT_LOOP_MONITOR_ENABLED` | No | 1 | Node.js event loop lag monitor. |
204
216
 
217
+ ## Webhooks
218
+
219
+ Hosted webhooks are off on a self-hosted instance until you turn them on:
220
+
221
+ 1. Run a Redis (or Valkey) cluster for webhooks and point `WEBHOOK_REDIS_HOST` and `WEBHOOK_REDIS_PORT` at one of its nodes. The webhook engine connects in cluster mode, so a standalone Redis won't work.
222
+ 2. Set `WEBHOOK_ENABLED=1` on the webapp.
223
+ 3. Give organizations access with the `hasWebhooksAccess` feature flag: turn it on for everyone on the admin dashboard's **Global Feature Flags** page, or for one organization from its feature flags on the **Organizations** page. An organization without it doesn't see Webhooks in the dashboard, and its deploys don't sync endpoints.
224
+
225
+ Webhook URLs are served from `WEBHOOK_INGRESS_ORIGIN`, which defaults to `API_ORIGIN`, so providers need to reach that origin from the internet.
226
+
227
+ The Docker Compose setup only passes the variables listed under the webapp service's `environment` in `hosting/docker/webapp/docker-compose.yml`, so add the webhook variables there. With the Helm chart, set them in `webapp.extraEnvVars`.
228
+
205
229
  ## Multi-Provider Object Storage
206
230
 
207
231
  The object storage system supports multiple S3-compatible providers (R2, S3, GCS, MinIO, etc.) using protocol prefixes. This enables migrating between providers without breaking existing runs.
@@ -121,6 +121,8 @@ You own a custom base image. It must provide:
121
121
 
122
122
  A `DEPLOY_BUILD_BASE_IMAGES` image needs everything the base image provides, plus `python3`, `make` and `g++`. Build extensions that add image instructions are replayed on it. Without an entry, the build stage uses the published build image, except for projects whose build extensions add image instructions: those build from your base image and install the toolchain with `apt-get`, so that base must be Debian-based. To avoid the published images entirely, set both variables.
123
123
 
124
+ A custom image is yours to harden. Deploys on the published images remove trust in publicly compromised certificate authorities and point Node.js and Python at the OS trust store. Stages built on a custom image get neither, and run with the certificate settings the image ships with. Keeping a custom image patched, trust store included, is your responsibility.
125
+
124
126
  <Warning>
125
127
  `image.pkgs` and build extensions that run `apt-get` (such as `aptGet` and `playwright`) assume a
126
128
  Debian base. On other distributions, install those packages in your base image instead.
@@ -4,10 +4,6 @@ description: "Point Slack (or any chat surface) at an agent: messages become tur
4
4
  sidebarTitle: "Channels"
5
5
  ---
6
6
 
7
- import WebhooksBetaBanner from "/snippets/webhooks-beta-banner.mdx";
8
-
9
- <WebhooksBetaBanner />
10
-
11
7
  A [session route](/webhooks/session-routing) delivers a verified event to an agent as an [action](/ai-chat/actions): the agent reacts, and the response is a side effect. A **channel** is the other half: the webhook IS the chat surface. Inbound messages become **turns** (the normal `run()` loop), and the agent's reply is posted **back** to the surface. A Slack thread becomes a real conversation with the agent, exactly like the browser chat, just a different frontend.
12
8
 
13
9
  List channels on a [`chat.agent`](/ai-chat/overview) alongside (or instead of) `events`:
@@ -15,14 +11,21 @@ List channels on a [`chat.agent`](/ai-chat/overview) alongside (or instead of) `
15
11
  ```ts
16
12
  import { webhooks } from "@trigger.dev/sdk";
17
13
  import { chat } from "@trigger.dev/sdk/ai";
18
- import { slack, webhookSource } from "@trigger.dev/slack";
14
+ import { appMentions, slack, webhookSource } from "@trigger.dev/slack";
19
15
  import { anthropic } from "@ai-sdk/anthropic";
20
16
 
21
17
  const slackEvents = webhooks.endpoint.define({ id: "slack", source: webhookSource() });
22
18
 
23
19
  export const supportAgent = chat.agent({
24
20
  id: "support-agent",
25
- channels: [slack({ id: "support-slack", endpoint: slackEvents, token: process.env.SLACK_BOT_TOKEN! })],
21
+ channels: [
22
+ slack({
23
+ id: "support-slack",
24
+ endpoint: slackEvents,
25
+ token: process.env.SLACK_BOT_TOKEN!,
26
+ startOn: appMentions(),
27
+ }),
28
+ ],
26
29
  run: async ({ messages, streamText }) =>
27
30
  streamText({ model: anthropic("claude-sonnet-4-5"), messages }),
28
31
  });
@@ -74,6 +77,25 @@ An event that `inbound` maps to an empty message (no text, and no file or data p
74
77
 
75
78
  List the connector in `chat.agent({ channels: [mySurface] })`, which subscribes the agent to the endpoint with the id `agentId:connectorId`. Other subscribers on the endpoint keep receiving its deliveries.
76
79
 
80
+ ## When a turn fails
81
+
82
+ When a turn fails (the model or provider returns an error, or the turn throws), the channel shows a short line instead of the error: "Something went wrong answering this. Try again." by default, or the connector's `errorMessage`. If the reply had partly streamed, the line goes after that text. The error is logged on the run, and `outbound` receives it as `reply.error`, so you can render a failure differently.
83
+
84
+ `errorMessage` is a fixed string, or a function that builds the line from the failure. It receives the `error`, the `partialText` streamed before it failed, the triggering `event` (undefined on a turn the channel didn't start), the `runId`, the `chatId`, and the `defaultMessage`. Return nothing, or throw, to fall back to the default line.
85
+
86
+ ```ts
87
+ slack({
88
+ id: "slack",
89
+ endpoint: slackEvents,
90
+ token: process.env.SLACK_BOT_TOKEN!,
91
+ startOn: appMentions(),
92
+ errorMessage: ({ error, runId }) =>
93
+ error.message.includes("rate limit")
94
+ ? "I'm getting a lot of requests right now. Try again in a minute."
95
+ : `Something went wrong answering this. Try again, or share ref ${runId} with support.`,
96
+ });
97
+ ```
98
+
77
99
  ## Stopping a turn from the channel
78
100
 
79
101
  A connector can map an event to a control with `onControl`. Returning `{ kind: "stop" }` while a turn is running aborts it through the run's `stopSignal`, exactly like the browser's stop button: the partial reply posts with `stopped: true` and the event is consumed instead of running as a turn. `linear()` and `slack()` map their stop buttons this way; a custom connector might map a `/stop` command:
@@ -92,7 +114,7 @@ When nothing is running, the event is handled by `inbound` as an ordinary messag
92
114
  Two more hooks let a connector mirror the session into a surface that has its own notion of an agent, the way `slack()` does:
93
115
 
94
116
  - `lifecycle(state, ctx)` reports `working` when a turn starts (and every 50 minutes while it runs), `waiting` when it pauses on a human decision, `idle` when it ends for any reason, and `closed` on `chat.close()`. `ctx.firstTurn` and `ctx.prompt` are there to title a new conversation. Calls are serialized, so an older report never lands after a newer one.
95
- - `stream(ctx)`, with `delivery: "stream"`, returns a per-turn transport `{ text(delta), progress?(event), end(outcome) }`. The runtime feeds it the reply's markdown deltas and tool progress, then calls `end({ stopped, pending, errorText? })` once. Resolve `{ delivered: true }` when the reply reached the surface, and the runtime does not post it again. Call `ctx.opened(ref)` once the surface has created the stream message: the ref is saved with the session while the turn runs, and if the worker dies mid-stream the next one calls the connector's `endInterruptedStream(ref, ctx)` before it answers, so the old stream does not look like it is still being written.
117
+ - `stream(ctx)`, with `delivery: "stream"`, returns a per-turn transport `{ text(delta), progress?(event), end(outcome) }`. The runtime feeds it the reply's markdown deltas and tool progress, then calls `end({ stopped, pending, errorText? })` once, with the connector's error line as `errorText` when the turn failed. Resolve `{ delivered: true }` when the reply reached the surface, and the runtime does not post it again. Call `ctx.opened(ref)` once the surface has created the stream message: the ref is saved with the session while the turn runs, and if the worker dies mid-stream the next one calls the connector's `endInterruptedStream(ref, ctx)` before it answers, so the old stream does not look like it is still being written.
96
118
 
97
119
  ## Bound sessions
98
120
 
@@ -4,10 +4,6 @@ description: "Register an endpoint's URL and signing secret with the provider, b
4
4
  sidebarTitle: "Connect a provider"
5
5
  ---
6
6
 
7
- import WebhooksBetaBanner from "/snippets/webhooks-beta-banner.mdx";
8
-
9
- <WebhooksBetaBanner />
10
-
11
7
  Connect a provider to an [endpoint](/webhooks/overview) by registering the endpoint's URL with the provider and giving Trigger.dev the credential that verifies its requests.
12
8
 
13
9
  Deploy the project or start `trigger dev` first. Once the CLI finds the endpoint, it appears on the environment's **Webhooks** page. The **Signing secret** column shows **Ready** when the endpoint can verify deliveries and **Needs secret** until then. Every delivery is rejected while an endpoint needs its secret.
@@ -22,8 +18,8 @@ The prompt tells the agent to use the Trigger.dev MCP server (`npx trigger.dev@l
22
18
  | --- | --- |
23
19
  | `list_webhook_endpoints` | Lists the environment's endpoints, with their URL and whether a secret is set. |
24
20
  | `get_webhook_endpoint_details` | Reads one endpoint: URL, subscribers and their filters, secret state, and the setup prompt. |
25
- | `generate_webhook_secret` | Generates a signing secret and returns it once, for providers where you choose the secret. |
26
- | `set_webhook_secret` | Stores a secret the provider issued. |
21
+ | `generate_webhook_secret` | Generates a signing secret and returns it once, for providers where you choose the secret. Optionally keeps accepting the old secret (`previousSecretTtlSeconds`). |
22
+ | `set_webhook_secret` | Stores a secret the provider issued. Optionally keeps accepting the old secret (`previousSecretTtlSeconds`). |
27
23
 
28
24
  Without the MCP server, the same operations are available on the REST API with the environment's secret key:
29
25
 
@@ -78,7 +74,18 @@ The verify token answers the URL check. The signing secret verifies event delive
78
74
 
79
75
  Choose **Rotate signing secret** to replace a provider-supplied secret. For a secret you generate, choose **Regenerate secret**, copy the new value, and update the provider's configuration. Use **Rotate public key** when replacing an asymmetric provider's key.
80
76
 
77
+ Both dialogs ask how long to keep accepting the old secret: **Stop immediately**, 1 hour, 24 hours (the default) or 7 days. Until then, deliveries signed with either secret verify, so you can update the provider without failing any. Choose **Stop immediately** if the old secret leaked.
78
+
79
+ Through the MCP tools or the REST API, pass `previousSecretTtlSeconds` (at most 604800, 7 days). The response's `previousSecretExpiresAt` says when the old secret stops working. Without it, the old secret stops working straight away:
80
+
81
+ ```bash
82
+ curl -X POST "https://api.trigger.dev/api/v1/webhooks/endpoints/github/rotate-secret" \
83
+ -H "Authorization: Bearer $TRIGGER_SECRET_KEY" \
84
+ -H "Content-Type: application/json" \
85
+ -d '{"previousSecretTtlSeconds": 3600}'
86
+ ```
87
+
81
88
  <Warning>
82
89
  Trigger.dev stores credentials encrypted and doesn't display saved values. Copy a generated secret
83
- before closing the dialog. Deliveries signed with the old secret fail verification after rotation.
90
+ before closing the dialog.
84
91
  </Warning>
@@ -4,10 +4,6 @@ description: "Inspect webhook payloads, where each delivery went, and the runs i
4
4
  sidebarTitle: "Deliveries and endpoints"
5
5
  ---
6
6
 
7
- import WebhooksBetaBanner from "/snippets/webhooks-beta-banner.mdx";
8
-
9
- <WebhooksBetaBanner />
10
-
11
7
  **A delivery records a verified webhook event and what happened to it at each subscriber.** Find deliveries on an endpoint's **Deliveries** tab. A run that a delivery triggered links back to it, and to its endpoint, from the run's **Detail** tab.
12
8
 
13
9
  ## Endpoints
@@ -85,7 +81,14 @@ Trigger.dev uses the endpoint and idempotency key to deduplicate incoming events
85
81
 
86
82
  A retry after the delivery window expires can create another delivery record. If the task-run idempotency window is still active, routing returns the existing run instead of starting another one.
87
83
 
88
- When the source maps a provider event ID, that ID becomes the key. For example, Stripe uses the event body's `id`, and GitHub uses the `X-GitHub-Delivery` header.
84
+ When the source maps a provider event ID, that ID becomes the key, so the provider's own retries of an event are recognised as duplicates:
85
+
86
+ | Key | Sources |
87
+ | --- | --- |
88
+ | A header | GitHub (`X-GitHub-Delivery`), Svix and Autumn (`svix-id`), Standard Webhooks, Loops and Granola (`webhook-id`), Linear (`Linear-Delivery`), PagerDuty (`X-Webhook-Id`), Jira (`X-Atlassian-Webhook-Identifier`), Zendesk (`X-Zendesk-Webhook-Invocation-Id`), Attio (`Idempotency-Key`), Shopify (`X-Shopify-Webhook-Id`), Hookdeck (`X-Hookdeck-Eventid`, or `<prefix>-eventid` with a custom header prefix) |
89
+ | A body field | Stripe, Notion, Vercel, Intercom, WorkOS, Metronome, Mux, Langfuse and Plain (`id`), Square, Slack and Typeform (`event_id`), Firecrawl (`webhookId`) |
90
+
91
+ The other named sources (Zoom, Calendly, Sentry, DocuSign, ElevenLabs, Vapi, Retell, Tailscale, Infisical, PlanetScale, Supabase, Discord) use the fallback below.
89
92
 
90
93
  If the source doesn't map an event ID, the key is derived from the raw body, timestamp, and signature. A retry with a different timestamp or signature can therefore produce a different key. For a [custom source](/webhooks/sources#custom-providers), set `idempotencyField` to a stable event identifier when the provider supplies one.
91
94
 
@@ -4,10 +4,6 @@ description: "Choose which verified webhook events reach each subscriber, by pay
4
4
  sidebarTitle: "Filters"
5
5
  ---
6
6
 
7
- import WebhooksBetaBanner from "/snippets/webhooks-beta-banner.mdx";
8
-
9
- <WebhooksBetaBanner />
10
-
11
7
  **A filter selects which verified deliveries reach a subscriber.** Each subscriber on an [endpoint](/webhooks/overview) has its own filter, so one delivery can start one task and skip another. A delivery no subscriber takes is recorded with status `FILTERED`, so you can inspect it in the dashboard.
12
8
 
13
9
  ## Add a filter
@@ -98,6 +94,8 @@ Paths must have non-empty segments. `event.data.amount` is valid; `event.`, `eve
98
94
 
99
95
  Values are strings in single quotes (`'created'`), numbers (`10000`), booleans (`true`), or `null`. Use a list of these values with `in` and `not in`.
100
96
 
97
+ A `null` value checks presence. `event.issue.pull_request != null` matches events that have the field, and `== null` matches events where it is missing or `null`. It works on any field, including fields your event type marks as required, because a missing field compares equal to `null`.
98
+
101
99
  ### Comparing two fields
102
100
 
103
101
  Equality and comparison operators also accept a field path on the right-hand side:
@@ -114,6 +112,12 @@ event.billing.country == event.shipping.country
114
112
  event.items any ( quantity > 0 )
115
113
  ```
116
114
 
115
+ When the payload itself is an array, as with providers that batch several events into one request (Tailscale, for example), quantify over `event`:
116
+
117
+ ```text Filter expression
118
+ event any ( type == 'nodeCreated' )
119
+ ```
120
+
117
121
  ### Spacing
118
122
 
119
123
  Separate paths, operators, and values with one space. Parentheses may touch what they enclose, as in `(event.amount > 0)` and `any (quantity > 0)`. Lists and quoted strings may contain spaces (`['opened', 'closed']`, `'Fix the build'`). These forms work with both the TypeScript validator and the deployment parser.
@@ -4,10 +4,6 @@ sidebarTitle: "Human-in-the-loop"
4
4
  description: "Pause an agent mid-turn to get a human decision from the channel (Slack approve/deny), then resume with the answer."
5
5
  ---
6
6
 
7
- import WebhooksBetaBanner from "/snippets/webhooks-beta-banner.mdx";
8
-
9
- <WebhooksBetaBanner />
10
-
11
7
  **An agent on a [channel](/webhooks/channels) can pause a turn to get a human decision, then resume once someone answers in the thread.** It posts controls into the conversation and picks up where it left off once someone clicks, with the decision merged into the turn. This is the channel counterpart of browser [human-in-the-loop](/ai-chat/patterns/human-in-the-loop): the same no-`execute` tool, but the controls live in Slack (or your own surface) instead of a React component. The [`slack()`](/webhooks/slack) connector ships Approve and Deny buttons out of the box.
12
8
 
13
9
  ## How it works
@@ -41,7 +37,7 @@ Out of the box, `slack()` posts Approve and Deny buttons for any pending no-`exe
41
37
  ```ts trigger/support-agent.ts
42
38
  import { webhooks } from "@trigger.dev/sdk";
43
39
  import { chat } from "@trigger.dev/sdk/ai";
44
- import { slack, webhookSource } from "@trigger.dev/slack";
40
+ import { appMentions, slack, webhookSource } from "@trigger.dev/slack";
45
41
  import { tool } from "ai";
46
42
  import { anthropic } from "@ai-sdk/anthropic";
47
43
  import { z } from "zod";
@@ -60,7 +56,14 @@ const requestApproval = tool({
60
56
 
61
57
  export const supportAgent = chat.agent({
62
58
  id: "support-agent",
63
- channels: [slack({ id: "support-slack", endpoint: slackEvents, token: process.env.SLACK_BOT_TOKEN! })],
59
+ channels: [
60
+ slack({
61
+ id: "support-slack",
62
+ endpoint: slackEvents,
63
+ token: process.env.SLACK_BOT_TOKEN!,
64
+ startOn: appMentions(),
65
+ }),
66
+ ],
64
67
  tools: { requestApproval },
65
68
  run: async ({ messages, tools, streamText }) =>
66
69
  streamText({
@@ -4,10 +4,6 @@ description: "Turn a chat.agent into a Linear agent: assign it an issue or @ment
4
4
  sidebarTitle: "Linear agents"
5
5
  ---
6
6
 
7
- import WebhooksBetaBanner from "/snippets/webhooks-beta-banner.mdx";
8
-
9
- <WebhooksBetaBanner />
10
-
11
7
  Linear opens an **agent session** when someone @mentions your app in an issue or delegates (assigns) an issue to it. `linear()` from `@trigger.dev/linear` is a [channel](/webhooks/channels) connector for those sessions: each one becomes a durable Trigger.dev [session](/ai-chat/sessions), the user's prompt runs as a turn, and the agent's work shows up in Linear as thoughts, actions, a plan, an approval card when it needs a decision, and a response when it is done.
12
8
 
13
9
  ```ts
@@ -67,7 +63,7 @@ linear({ id: "linear", endpoint: linearEvents, token, context: { guidance: false
67
63
  | Turn starts | An ephemeral `thought` ("Looking into this...") |
68
64
  | A tool is called | An ephemeral `action` with the tool name and input, then the same action with its `result` |
69
65
  | The reply is ready | A `response` with the reply as markdown |
70
- | The turn fails | An `error` |
66
+ | The turn fails | An `error` saying "Something went wrong answering this. Try again." (`errorMessage` changes it) |
71
67
  | A tool needs a human decision | An `elicitation` with Approve and Deny options, or the tool's own options from `interactions` (see [Approvals and choices](#approvals-and-choices)) |
72
68
  | The user picks an option | A `thought` recording the decision ("Approved by Jane.", "Jane chose "High".") |
73
69
  | A prompt arrives while the agent works | A `thought` ("Got it, I'll take that into account.") as soon as it arrives (see [Prompts sent while the agent is working](#prompts-sent-while-the-agent-is-working)) |
@@ -176,6 +172,7 @@ The first Linear turn binds the session to the agent session ([bound sessions](/
176
172
  | `approvals` | `{ [toolName]: (input, call) => { title?, body } }` | Custom approval card body per tool. Default lays the tool input out as markdown. |
177
173
  | `interactions` | `{ [toolName]: (call) => { title?, body, options } }` | A tool's own select card; the chosen option resolves the tool to `{ selected }`. |
178
174
  | `receivedThought` | `string`, function or `null` | Thought posted as soon as a prompt arrives while the agent is working. Default "Got it, I'll take that into account.", null for none. |
175
+ | `errorMessage` | `string \| (ctx) => string` | The error activity's text when a turn fails, fixed or built from the failure ([details](/webhooks/channels#when-a-turn-fails)). Default "Something went wrong answering this. Try again." |
179
176
  | `ack` | message, `null`, or function | The first thought. Pass `null` to post nothing before the response. |
180
177
  | `inbound` / `outbound` | functions | Map the event to the turn, and the reply to an activity. |
181
178
  | `delivery` | `"final"` or `"stream"` | `"final"` (default) posts the response when the turn ends. `"stream"` also posts ephemeral thoughts as the reply streams. |