You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: idempotency keys deduplicate triggers, not side effects inside run()
The idempotency page listed avoiding double charges as a use case, but its
examples only ever deduplicate a child trigger. A payment call made directly
inside a retryable run() gets no protection from the Trigger.dev key. The
page now states the boundary up front and adds a section showing the
provider's own idempotency key (Stripe) alongside the Trigger.dev key.
Copy file name to clipboardExpand all lines: docs/idempotency.mdx
+41-5Lines changed: 41 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,7 +3,9 @@ title: "Idempotency"
3
3
description: "An API call or operation is idempotent if it has the same result when called more than once."
4
4
---
5
5
6
-
We currently support idempotency at the task level, meaning that if you trigger a task with the same `idempotencyKey` twice, the second request will not create a new task run. Instead, the original run's handle is returned, allowing you to track the existing run's progress.
6
+
**Idempotency keys deduplicate task triggers.** If you trigger a task with the same `idempotencyKey` twice, the second request does not create a new run. It returns the original run's handle, so you can track the existing run's progress.
7
+
8
+
The key applies to the trigger call and nothing else. It deduplicates `trigger()`, `triggerAndWait()`, and `batchTrigger()`; it does not make the code inside a task's `run()` function idempotent. See [Side effects inside `run()`](#side-effects-inside-run) for what that means when a task calls an external API.
7
9
8
10
## Why use idempotency keys?
9
11
@@ -31,10 +33,44 @@ sequenceDiagram
31
33
32
34
Other common use cases include:
33
35
34
-
-**Preventing duplicate emails** - Ensure a confirmation email is only sent once, even if the parent task retries
35
-
-**Avoiding double-charging customers** - Prevent duplicate payment processing during retries
36
-
-**One-time setup tasks** - Ensure initialization or migration tasks only run once
37
-
-**Deduplicating webhook processing** - Handle the same webhook event only once, even if it's delivered multiple times
36
+
-**Preventing duplicate emails** - Trigger the email task once, even if the parent task retries
37
+
-**Avoiding double-charging customers** - Trigger the payment task once, even if the parent task retries. The payment API call itself still needs the provider's own idempotency key, see [Side effects inside `run()`](#side-effects-inside-run)
38
+
-**One-time setup tasks** - Trigger initialization or migration tasks once
39
+
-**Deduplicating webhook processing** - Trigger the handler task once per webhook event, even if the event is delivered multiple times
40
+
41
+
## Side effects inside `run()`
42
+
43
+
An idempotency key stops a task from being *triggered* twice. It does not stop the code inside that task from *running* twice. When a run retries, `run()` executes again from the top, and every API call, database write, or email inside it happens again unless that operation is itself idempotent.
44
+
45
+
This matters most for payments. A refund issued directly inside a retryable task is issued again on every retry, even when the retry is caused by an unrelated failure later in the same function. Moving the refund into a child task and triggering it with an idempotency key does not close the gap either: the child is triggered once, but its own `run()` can retry after the provider has accepted the request. Only the provider can deduplicate its own API call.
46
+
47
+
Pass the provider's idempotency key on every call that must happen at most once. Derive it from a value that stays the same across retries, such as an ID from your payload:
Use a Trigger.dev idempotency key to stop a retry triggering a task twice, and the provider's idempotency key to stop a retry calling the provider twice. For a side effect with no idempotency support, record that you performed it in your own database and check that record before performing it again.
0 commit comments