Skip to content

docs: clarify idempotency for external side effects - #4650

Closed
bharathkumar39293 wants to merge 1 commit into
triggerdotdev:mainfrom
bharathkumar39293:docs/idempotency-external-side-effects
Closed

docs: clarify idempotency for external side effects#4650
bharathkumar39293 wants to merge 1 commit into
triggerdotdev:mainfrom
bharathkumar39293:docs/idempotency-external-side-effects

Conversation

@bharathkumar39293

Copy link
Copy Markdown
Contributor

Summary

  • Clarify that Trigger.dev idempotency keys deduplicate task-trigger requests, not arbitrary external side effects.
  • Add guidance for combining Trigger.dev task idempotency with an external payment provider’s idempotency mechanism.
  • Add a Stripe refund example.

Validation

  • git diff --check passes.
  • Documentation-only change.

Changelog

Clarify idempotency guidance for external side effects.

@changeset-bot

changeset-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: a80eda6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Updated the idempotency documentation to describe coordination between Trigger.dev task deduplication and external provider idempotency. Added guidance that task deduplication does not guarantee exactly-once external side effects. Added refund examples that derive Trigger.dev and Stripe idempotency keys from a deterministic business operation ID. Documented retries or crashes after provider request acceptance.

Merge Risk: 🟡 Moderate · up to a80ed

The documentation could cause duplicate refunds if a task replays after Stripe has pruned its idempotency key; merge should wait for the retention limit and reconciliation guidance to be added or explicitly accepted.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the documentation change about idempotency for external side effects.
Description check ✅ Passed The description explains the change, validation, and changelog, but omits the issue reference, checklist, and screenshots sections.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution! We require all external PRs to be opened in draft status first so you can address CodeRabbit review comments and ensure CI passes before requesting a review. Please re-open this PR as a draft. See CONTRIBUTING.md for details.

@github-actions github-actions Bot closed this Aug 17, 2026

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment thread docs/idempotency.mdx
Comment on lines +75 to +129
```ts trigger/refund.ts
import { task } from "@trigger.dev/sdk";
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export const refundPayment = task({
id: "refund-payment",
run: async (payload: {
paymentIntentId: string;
amount: number;
operationId: string;
}) => {
await stripe.refunds.create(
{
payment_intent: payload.paymentIntentId,
amount: payload.amount,
},
{
// Stripe uses this key to make retries of the same request idempotent.
idempotencyKey: payload.operationId,
}
);
},
});
```

```ts trigger/initiate-refund.ts
import { task } from "@trigger.dev/sdk";
import { refundPayment } from "./refund";

export const initiateRefund = task({
id: "initiate-refund",
run: async (payload: {
refundId: string;
paymentIntentId: string;
amount: number;
}) => {
// Use an ID that identifies this business operation, not a random value.
const operationId = `refund:${payload.refundId}`;

await refundPayment.trigger(
{
paymentIntentId: payload.paymentIntentId,
amount: payload.amount,
operationId,
},
{
// Trigger.dev deduplicates this child task trigger when this task retries.
idempotencyKey: operationId,
}
);
},
});
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Two related file examples are shown as separate code blocks instead of a grouped multi-file example

The new two-file refund example is written as two stand-alone code blocks (starting at docs/idempotency.mdx:75) instead of the documented multi-file grouping component, so readers see the files as unrelated snippets rather than one example.
Impact: The docs render inconsistently with the rest of the site for multi-file examples, making the two-file walkthrough harder to follow.

Documentation convention for multi-file examples

docs/CLAUDE.md explicitly lists <CodeGroup> as the component to use for "Multi-language/multi-file code examples". The PR adds trigger/refund.ts (docs/idempotency.mdx:75-100) and trigger/initiate-refund.ts (docs/idempotency.mdx:102-129) back-to-back with filename tags, which is exactly the multi-file case; they should be wrapped in a <CodeGroup> block.

Prompt for agents
The two new code blocks in docs/idempotency.mdx (trigger/refund.ts and trigger/initiate-refund.ts) form a single multi-file example. Per docs/CLAUDE.md, multi-file code examples should be wrapped in a <CodeGroup> component so Mintlify renders them as tabs. Wrap the two consecutive fenced blocks in <CodeGroup> ... </CodeGroup>.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread docs/idempotency.mdx
Comment on lines +116 to +126
await refundPayment.trigger(
{
paymentIntentId: payload.paymentIntentId,
amount: payload.amount,
operationId,
},
{
// Trigger.dev deduplicates this child task trigger when this task retries.
idempotencyKey: operationId,
}
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Raw-string idempotency key in the child trigger is run-scoped, which limits the claimed deduplication

The example passes operationId as a raw string to refundPayment.trigger(...). As documented later in the same page (docs/idempotency.mdx:172-174), a raw string defaults to "run" scope, i.e. hashed with the parent run ID. So the deduplication only holds across retries of the same initiateRefund run — if initiateRefund is triggered twice for the same refundId, two child runs are created and only Stripe's own idempotency key prevents a double refund. The inline comment ("deduplicates this child task trigger when this task retries") is accurate, but the section intro at docs/idempotency.mdx:73 ("use a deterministic business-operation ID ... Trigger.dev deduplicates child task triggers") reads as if it dedupes globally. Consider using idempotencyKeys.create(operationId, { scope: "global" }) or stating the scope caveat explicitly.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dc1f2cc9-facf-470b-b973-ca433a78cb44

📥 Commits

Reviewing files that changed from the base of the PR and between 6e77102 and a80eda6.

📒 Files selected for processing (1)
  • docs/idempotency.mdx

Included review availability: Your plan includes up to 10 reviews per rolling hour; 9 remain after this review.

📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: code-quality / code-quality
  • GitHub Check: check-broken-links
🧰 Additional context used
📓 Path-based instructions (1)
docs/**/*.mdx

📄 CodeRabbit inference engine (docs/CLAUDE.md)

docs/**/*.mdx: MDX documentation pages must include frontmatter with title (required), description (required), and sidebarTitle (optional) in YAML format
Use Mintlify components for structured content: , , , , , , /, /
Always import from @trigger.dev/sdk in code examples (never from @trigger.dev/sdk/v3)
Code examples must be complete and runnable where possible
Use language tags in code fences: typescript, bash, json

Files:

  • docs/idempotency.mdx
🧠 Learnings (2)
📚 Learning: 2026-03-10T12:44:14.176Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3200
File: docs/config/config-file.mdx:353-368
Timestamp: 2026-03-10T12:44:14.176Z
Learning: In the trigger.dev repo, docs PRs are often companions to implementation PRs. When reviewing docs PRs (MDX files under docs/), check the PR description for any companion/related PR references and verify that the documented features exist in those companion PRs before flagging missing implementations. This ensures docs stay in sync with code changes across related PRs.

Applied to files:

  • docs/idempotency.mdx
📚 Learning: 2026-04-30T20:30:29.458Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3226
File: docs/ai-chat/quick-start.mdx:13-13
Timestamp: 2026-04-30T20:30:29.458Z
Learning: In this repo’s documentation MDX files (`docs/**/*.mdx`), use `ts` and `tsx` (not `typescript`) as the code-fence language tags for TypeScript/TSX snippets. Do not flag `ts`/`tsx` code-fence language tags as incorrect in any docs MDX file, since this is the site-wide Mintlify-compatible convention.

Applied to files:

  • docs/idempotency.mdx

Comment thread docs/idempotency.mdx
Comment on lines +94 to +95
// Stripe uses this key to make retries of the same request idempotent.
idempotencyKey: payload.operationId,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target excerpt ---'
sed -n '1,145p' docs/idempotency.mdx
printf '%s\n' '--- relevant references ---'
rg -n -C 3 'idempotency|operationId|refund|exactly.?once|Stripe' docs/idempotency.mdx

Repository: triggerdotdev/trigger.dev

Length of output: 27150


🌐 Web query:

Stripe API idempotent requests retention at least 24 hours reused key after pruning official documentation

💡 Result:

In the Stripe API, idempotency keys are retained for at least 24 hours [1][2]. After this retention period, Stripe automatically prunes or removes the keys from its system [1][2]. If you reuse an idempotency key after the original request's data has been pruned (i.e., after the 24-hour window), Stripe will treat the subsequent request as a new, unique request rather than a retry of the original [1][3]. When reusing an idempotency key, the Stripe idempotency layer performs a validation check: it compares the parameters of the incoming request against those of the original request [1][4]. If the parameters do not match the original request, Stripe will return an idempotency error to prevent accidental misuse [1][5]. If the parameters do match, the system returns the cached result of the original request, provided the key has not yet been pruned [1][2].

Citations:


Document Stripe’s idempotency retention limit.

Stripe retains API idempotency keys for at least 24 hours and can prune them afterward. If this task replays after pruning, reusing payload.operationId can create a second refund. State this limit. If a refund can replay later, persist the business operation and Stripe refund ID, then reconcile with Stripe before issuing another request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant