Repository navigation
docs(webhooks): pricing and per-plan webhook limits #5051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/sdk": patch | ||
| --- | ||
|
|
||
| The bundled `trigger-cost-savings` skill now notes that a webhook delivery no subscriber's filter matches starts no run and costs no compute. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -190,6 +190,10 @@ export const waitpointTask = task({ | |
|
|
||
| Waiting saves you compute, but it doesn't always free up concurrency — see [Wait](/wait). | ||
|
|
||
| ## Filter webhooks before they start runs | ||
|
|
||
| Give each [webhook](/webhooks/overview) subscriber a [filter](/webhooks/filters) so it only runs for the events it handles. A delivery that no filter matches is recorded as `FILTERED` and starts no run, so you aren't charged compute for it. Receiving webhooks has no separate charge. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Filtered subscribers do not prevent waiter compute When subscriber filters reject an event but a waiter matches, Learn moreWebhook waiters subscribe to an endpoint independently of task, agent, and channel subscribers. A matching waiter resumes its existing run even when all subscriber filters reject the event. The delivery then succeeds rather than settling as Example: A Stripe endpoint has a task filtered to Recommended fix: Qualify the zero-compute claim on there being no matching waiter as well as no matching subscriber; update the cost guide, pricing overview, and bundled skill together. Was this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
| ## Use debounce to consolidate multiple triggers | ||
|
|
||
| When a task might be triggered multiple times in quick succession, use debounce to consolidate them into a single run. This is useful for document indexing, webhook aggregation, cache invalidation, and real-time sync scenarios. | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -178,6 +178,46 @@ All of an environment's endpoints also share a per-environment limit, counted in | |||||
| | Pro | 3,000 every 10 sec | | ||||||
| | Enterprise | Set per organization | | ||||||
|
|
||||||
| ## Webhook waiters | ||||||
|
|
||||||
| A [waiter](/webhooks/waiters) is a run paused until a matching webhook arrives. The per-endpoint limit is also the most waiting runs one delivery can resume. | ||||||
|
|
||||||
| | Pricing tier | Live waiters per environment | Live waiters per endpoint | | ||||||
| | :----------- | :--------------------------- | :------------------------ | | ||||||
| | Free | 500 | 100 | | ||||||
| | Hobby | 2,000 | 1,000 | | ||||||
| | Pro | 25,000 | 10,000 | | ||||||
| | Enterprise | Set per organization | Set per organization | | ||||||
|
|
||||||
| A waiter can wait for up to 90 days. | ||||||
|
|
||||||
| ## Webhook processing concurrency | ||||||
|
|
||||||
| This limit controls how many webhook deliveries and waiter resumes are processed at once per environment. More wait their turn. It is not a limit on the runs those deliveries start, which use your normal [concurrency limits](#concurrency-limits). | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Clarify the processing-concurrency sentence. Replace “More wait their turn.” with “Additional deliveries wait their turn.” The current sentence is not grammatical and does not identify what waits. |
||||||
|
|
||||||
| | Pricing tier | Limit | | ||||||
| | :----------- | :----------------------- | | ||||||
| | Free | 5 concurrent deliveries | | ||||||
| | Hobby | 10 concurrent deliveries | | ||||||
| | Pro | 50 concurrent deliveries | | ||||||
|
Comment on lines
+198
to
+202
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win Document the Enterprise processing limit. Add an Enterprise row or state that Enterprise uses a separate organization-level setting. The other webhook limit tables document Enterprise explicitly, and the webhook limits configuration supports an optional |
||||||
|
|
||||||
| ## Webhook delivery retention | ||||||
|
|
||||||
| How long [deliveries](/webhooks/deliveries) stay visible in the dashboard and the API. | ||||||
|
|
||||||
| | Pricing tier | Limit | | ||||||
| | :----------- | :--------------------------- | | ||||||
| | Free | 3 days | | ||||||
| | Hobby | 7 days | | ||||||
| | Pro | 30 days | | ||||||
| | Enterprise | 90, 180 or 365 days | | ||||||
|
|
||||||
| Deliveries are stored for at least 30 days, so upgrading your plan shows older deliveries right away. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Strict retention removes upgrade history With Learn moreThe visible retention limit and physical storage duration are separate settings. resolveWebhookLimits selects the visible retention class for storage when Example: An organization with three-day visible retention and strict retention receives a delivery on Monday. Its delivery enters the three-day class; an upgrade several weeks later cannot reveal it. Recommended fix: Qualify the 30-day storage and upgrade promise with the condition that strict retention is not enabled, or describe the strict-retention exception explicitly. Was this helpful? React with 👍 or 👎 to provide feedback.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: set -o pipefail
printf '%s\n' '--- changed docs hunk ---'
git diff --no-ext-diff --unified=25 d9bf563ae3badd0620a88b85c6784c5a8a5c1b6d 451d78c9b54eda489cf9ba278f05ec68544bfcbf -- docs/limits.mdx
printf '%s\n' '--- retention identifier references ---'
rg -n -F --glob '!node_modules' --glob '!dist' --glob '!build' -e 'deliveryRetentionDays' -e 'deliveryStorageDays' -e 'deliveryRetentionStrict' -e 'Deliveries are stored for at least 30 days' .
printf '%s\n' '--- relevant file candidates ---'
rg --files | rg '(^|/)(limits|retention|delivery|webhook|cloud|plan|configuration|config)([^/]*)\.(ts|tsx|js|jsx|json|md|mdx|yaml|yml)$' | head -200Repository: triggerdotdev/trigger.dev Length of output: 13606 🏁 Script executed: set -o pipefail
printf '%s\n' '--- changed docs hunk ---'
git diff --no-ext-diff --unified=25 d9bf563ae3badd0620a88b85c6784c5a8a5c1b6d 451d78c9b54eda489cf9ba278f05ec68544bfcbf -- docs/limits.mdx
printf '%s\n' '--- retention identifier references ---'
rg -n -F --glob '!node_modules' --glob '!dist' --glob '!build' -e 'deliveryRetentionDays' -e 'deliveryStorageDays' -e 'deliveryRetentionStrict' -e 'Deliveries are stored for at least 30 days' .Repository: triggerdotdev/trigger.dev Length of output: 9882 🏁 Script executed: set -o pipefail
git diff --no-ext-diff --unified=25 d9bf563ae3badd0620a88b85c6784c5a8a5c1b6d 451d78c9b54eda489cf9ba278f05ec68544bfcbf -- docs/limits.mdx
rg -n -F --glob '!node_modules' --glob '!dist' --glob '!build' -e 'deliveryRetentionDays' -e 'deliveryStorageDays' -e 'deliveryRetentionStrict' -e 'Deliveries are stored for at least 30 days' .Repository: triggerdotdev/trigger.dev Length of output: 9816 🏁 Script executed: set -o pipefail
printf '%s\n' '--- resolver ---'
nl -ba apps/webapp/app/v3/webhookLimits.ts | sed -n '1,90p'
printf '%s\n' '--- default loading ---'
nl -ba apps/webapp/app/v3/webhookLimits.server.ts | sed -n '1,95p'
printf '%s\n' '--- webhook engine README contract ---'
nl -ba internal-packages/webhook-engine/README.md | sed -n '35,70p'
printf '%s\n' '--- retention environment/config declarations ---'
rg -n -F --glob '!node_modules' --glob '!dist' --glob '!build' -e 'WEBHOOK_DELIVERY_RETENTION_DAYS' -e 'WEBHOOK_DELIVERY_STORAGE_DAYS' -e 'WEBHOOK_DELIVERY_RETENTION_STRICT' -e 'deliveryRetentionStrict' -e 'deliveryStorageDays' -e 'deliveryRetentionDays' -- .env* apps internal-packages packages deploy infra docs 2>/dev/null || test "$?" -eq 1Repository: triggerdotdev/trigger.dev Length of output: 16811 🏁 Script executed: set -o pipefail
printf '%s\n' '--- environment defaults ---'
nl -ba apps/webapp/app/env.server.ts | sed -n '2112,2135p'
printf '%s\n' '--- admin retention controls ---'
nl -ba apps/webapp/app/components/admin/backOffice/WebhookLimitsSection.tsx | sed -n '135,195p'
nl -ba apps/webapp/app/components/admin/backOffice/WebhookLimitsForm.ts | sed -n '1,70p'
printf '%s\n' '--- webhook limits config persistence/references ---'
rg -n -F --glob '!node_modules' --glob '!dist' --glob '!build' -e 'webhookLimitsConfig' -e 'deliveryRetentionStrict' apps/webapp | head -160Repository: triggerdotdev/trigger.dev Length of output: 9546 Qualify the delivery storage claim. Strict retention changes the storage window. When it is disabled, deliveries remain stored for at least 30 days and an upgrade can reveal older deliveries that remain within their storage class. When it is enabled, new deliveries are stored only for the visible retention period rounded to a retention class, so an upgrade cannot recover deliveries already deleted. Suggested wording--- "a/docs/limits.mdx"
+++ "b/docs/limits.mdx"
@@ -212,7 +212,7 @@
| Pro | 30 days |
| Enterprise | 90, 180 or 365 days |
-Deliveries are stored for at least 30 days, so upgrading your plan shows older deliveries right away.
+When strict retention is disabled, deliveries are stored for at least 30 days and hidden after the visible retention, so upgrading can show older deliveries that remain within their storage class. When strict retention is enabled, new deliveries are stored only for the visible retention rounded to a retention class, so upgrading cannot recover deliveries deleted before the upgrade.
## Webhook subscribers
📝 Committable suggestion
Suggested change
|
||||||
|
|
||||||
| ## Webhook subscribers | ||||||
|
|
||||||
| An endpoint can have up to 25 subscribers (tasks, agent sessions and channels) on every plan. | ||||||
|
|
||||||
| ## Log retention | ||||||
|
|
||||||
| | Pricing tier | Limit | | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,6 +70,10 @@ The [MCP server](/mcp-introduction) has `list_webhook_deliveries` (filter by end | |
| | [`GET /api/v1/webhooks/deliveries/{deliveryId}`](/management/webhooks/retrieve-delivery) | Reads a delivery, with a `targets` entry per subscriber and one for waiting runs. | | ||
| | [`POST /api/v1/webhooks/deliveries/{deliveryId}/replay`](/management/webhooks/replay-delivery) | Replays it, as above. The key needs permission to trigger every task the replay starts, and to write sessions if it reaches an agent's subscriber. | | ||
|
|
||
| ## Retention | ||
|
|
||
| Deliveries stay visible for 3 days on Free, 7 days on Hobby and 30 days on Pro. See [Limits](/limits#webhook-delivery-retention). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Include Enterprise retention. Add the Enterprise retention values, or explicitly scope this sentence to Free, Hobby, and Pro. The limits page documents 90, 180, or 365 days for Enterprise, so this page currently gives Enterprise users an incomplete answer. |
||
|
|
||
| ## Deduplication | ||
|
|
||
| Trigger.dev uses the endpoint and idempotency key to deduplicate incoming events. Delivery records and task runs have separate deduplication windows: | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -83,6 +83,14 @@ The CLI finds your endpoints and subscribers during `trigger dev` and deployment | |||||||||||||
|
|
||||||||||||||
| A request must pass verification before anything else happens. Provider handshakes are answered directly and create no delivery. A verified request is checked for duplicates, recorded, and then routed: each subscriber's filter is checked, matching subscribers run, and waiting runs whose match fits the event are resumed. The delivery records a result per subscriber. | ||||||||||||||
|
|
||||||||||||||
| ## Pricing and limits | ||||||||||||||
|
|
||||||||||||||
| Webhooks have no separate charge on Trigger.dev Cloud. Receiving, verifying, filtering and recording deliveries is included in every plan. The runs a delivery starts or resumes are billed like any other run, by compute time. See [pricing](https://trigger.dev/pricing). | ||||||||||||||
|
|
||||||||||||||
| A delivery that no subscriber's filter matches starts no run, so it uses no compute. A run [waiting](/webhooks/waiters) for a webhook is suspended, so it uses no compute until the webhook arrives. | ||||||||||||||
|
Comment on lines
+88
to
+90
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: set -eu
printf '%s\n' '--- diff ---'
git diff --stat d9bf563ae3badd0620a88b85c6784c5a8a5c1b6d 451d78c9b54eda489cf9ba278f05ec68544bfcbf -- docs/webhooks/overview.mdx docs/webhooks/deliveries.mdx docs/limits.mdx docs/guides/spend.mdx skills/trigger-cost-savings || true
git diff --unified=25 d9bf563ae3badd0620a88b85c6784c5a8a5c1b6d 451d78c9b54eda489cf9ba278f05ec68544bfcbf -- docs/webhooks/overview.mdx docs/webhooks/deliveries.mdx docs/limits.mdx docs/guides/spend.mdx skills/trigger-cost-savings || true
printf '%s\n' '--- relevant repository text ---'
rg -n -i --glob '!node_modules' --glob '!dist' --glob '!build' -e 'webhook.*(bill|charg|compute|filter)' -e '(bill|charg|compute).*webhook' -e 'FILTERED' -e 'webhook-limits-backfill' -e 'webhook.*limit' . || test "$?" -eq 1
printf '%s\n' '--- changed overview context ---'
nl -ba docs/webhooks/overview.mdx | sed -n '70,110p'
printf '%s\n' '--- changed spend/skill references ---'
rg -n -C 5 -i --glob '*.md' --glob '*.mdx' --glob '*.ts' --glob '*.json' --glob '*.yaml' --glob '*.yml' -e 'webhooks have no separate charge' -e 'no separate charge' -e 'filtered deliveries' -e 'webhook.*compute' -e 'webhook.*pricing' . || test "$?" -eq 1Repository: triggerdotdev/trigger.dev Length of output: 45664 🏁 Script executed: set -eu
printf '%s\n' '--- webhook billing and ingress references ---'
rg -n -i --glob 'apps/**' --glob 'internal-packages/**' --glob 'packages/**' --glob 'docs/**' --glob '!**/node_modules/**' -e 'chargeWebhook' -e 'webhook.*billing' -e 'billing.*webhook' -e 'webhook.*price' -e 'price.*webhook' -e 'compute.*charge' -e 'charge.*compute' -e 'Webhook.*Billing' .
printf '%s\n' '--- ingress limiter implementation ---'
nl -ba apps/webapp/app/services/webhookIngressRateLimit.server.ts | sed -n '1,240p'
printf '%s\n' '--- webhook engine billing-adjacent declarations ---'
nl -ba internal-packages/webhook-engine/src/engine/index.ts | sed -n '230,330p'
nl -ba internal-packages/webhook-engine/src/engine/index.ts | sed -n '520,620p'
printf '%s\n' '--- local pricing references ---'
rg -n -i --glob 'docs/**' --glob 'apps/**' --glob 'internal-packages/**' --glob 'packages/**' -e 'pricing' -e 'included in every plan' -e 'no separate' -e 'billed like any other run' .
printf '%s\n' '--- exact changed files ---'
git diff --name-status d9bf563ae3badd0620a88b85c6784c5a8a5c1b6d 451d78c9b54eda489cf9ba278f05ec68544bfcbfRepository: triggerdotdev/trigger.dev Length of output: 41893 🌐 Web query:
💡 Result: Describe the standard run fee as well as compute charges.
Suggested fix--- "a/docs/webhooks/overview.mdx"
+++ "b/docs/webhooks/overview.mdx"
@@ -85,7 +85,7 @@
## Pricing and limits
-Webhooks have no separate charge on Trigger.dev Cloud. Receiving, verifying, filtering and recording deliveries is included in every plan. The runs a delivery starts or resumes are billed like any other run, by compute time. See [pricing](https://trigger.dev/pricing).
+Webhooks have no separate charge on Trigger.dev Cloud. Receiving, verifying, filtering and recording deliveries is included in every plan. The runs a delivery starts or resumes use normal run pricing: a run that starts executing incurs the standard invocation fee, and execution time incurs compute charges. See [pricing](https://trigger.dev/pricing).
A delivery that no subscriber's filter matches starts no run, so it uses no compute. A run [waiting](/webhooks/waiters) for a webhook is suspended, so it uses no compute until the webhook arrives.
📝 Committable suggestion
Suggested change
|
||||||||||||||
|
|
||||||||||||||
| Each plan sets limits on the ingest rate, waiting runs, processing concurrency and how long deliveries are kept. See [Limits](/limits#webhook-ingest-rate-limits). | ||||||||||||||
|
|
||||||||||||||
| ## Connect it | ||||||||||||||
|
|
||||||||||||||
| Every endpoint can produce a setup prompt for an AI coding agent. It holds the URL, the events your subscribers filter on, and the steps for registering the endpoint and its signing secret with the provider. Copy it from the endpoints list or the endpoint's page, or read it with the MCP server's `get_webhook_endpoint_details` tool. See [Connect a provider](/webhooks/connect). | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -185,6 +185,6 @@ if (!result.cancelled) { | |
| | Tags per waiter | 10 | | ||
| | Timeout | 24 hours by default, 90 days at most | | ||
|
|
||
| On Trigger.dev Cloud the per-endpoint and per-environment limits depend on your plan. Self-hosted installs can change them with `WEBHOOK_WAITER_MAX_PER_ENDPOINT`, `WEBHOOK_WAITER_MAX_PER_ENVIRONMENT` and `WEBHOOK_WAITER_MAX_SHAPES`. | ||
| On Trigger.dev Cloud the per-endpoint and per-environment limits depend on your plan: see [Limits](/limits#webhook-waiters). Self-hosted installs can change them with `WEBHOOK_WAITER_MAX_PER_ENDPOINT`, `WEBHOOK_WAITER_MAX_PER_ENVIRONMENT` and `WEBHOOK_WAITER_MAX_SHAPES`. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Clarify the scope of the preceding limits table. The table lists 10,000 live waiters per endpoint as the default, while the linked Cloud table lists 100 for Free and 1,000 for Hobby. Label the table as self-hosted defaults or separate Cloud and self-hosted values. |
||
|
|
||
| A delivery that resumes waiters shows one **Waiting runs** row on its [delivery page](/webhooks/deliveries#where-a-delivery-went) with how many matched, resumed and failed. A delivery that arrives while runs are waiting but matches none of them, and no subscriber takes, is `UNMATCHED`. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix the changeset sentence.
The phrase
a webhook delivery no subscriber's filter matches starts no runis not grammatical. Usea webhook delivery starts no run when no subscriber's filter matches.