> ## Documentation Index
> Fetch the complete documentation index at: https://docs.threadify.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency

> Ensuring consistent step recordings in Threadify

**Idempotency** ensures that recording the same step multiple times has the same effect as recording it once. This prevents duplicate operations and ensures consistent execution when retries or network issues occur.

### Idempotency Approaches

| Approach                         | When to Use                                                              | Key Generation                                   |
| -------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------ |
| **Auto-generated** (default)     | Context uniquely identifies operation                                    | Hash of `stepName + context`                     |
| **Manual** (`.idempotencyKey()`) | External system IDs, user-initiated retries, cross-service deduplication | Custom key (e.g., Stripe payment ID, request ID) |

<CodeGroup>
  ```javascript JavaScript theme={null}
  // Auto-generated (default)
  await thread.step('validate_cart')
    .addContext({ items: '3', total: '99.99' })
    .success();

  // Manual key with external ID
  const payment = await stripe.charges.create({ amount: 9999 });
  await thread.step('charge_payment')
    .idempotencyKey(payment.id)
    .addContext({ amount: '99.99' })
    .success();
  ```

  ```go Go theme={null}
  // Auto-generated (default)
  _, err := thread.Step("validate_cart").
      AddContext(map[string]any{"items": "3", "total": "99.99"}).
      Success(ctx)
  if err != nil {
      log.Fatal(err)
  }

  // Manual key with external ID
  payment, err := stripe.Charges.Create(&stripe.ChargeParams{Amount: 9999})
  if err != nil {
      log.Fatal(err)
  }
  _, err = thread.Step("charge_payment").
      IdempotencyKey(payment.ID).
      AddContext(map[string]any{"amount": "99.99"}).
      Success(ctx)
  if err != nil {
      log.Fatal(err)
  }
  ```

  ```python Python theme={null}
  # Auto-generated (default)
  await (
      thread.step("validate_cart")
      .add_context({"items": "3", "total": "99.99"})
      .success()
  )

  # Manual key with external ID
  payment = await stripe.charges.create(amount=9999)
  await (
      thread.step("charge_payment")
      .idempotency_key(payment.id)
      .add_context({"amount": "99.99"})
      .success()
  )
  ```
</CodeGroup>

### Use Cases

| Scenario                 | Behavior                              | Example                             |
| ------------------------ | ------------------------------------- | ----------------------------------- |
| **Network retries**      | Second call with same key is ignored  | Payment processing retry            |
| **Service restarts**     | Same key returns existing result      | Service crashes mid-operation       |
| **Race conditions**      | Only first request succeeds           | Multiple pods processing same order |
| **Duplicate prevention** | Prevents duplicate charges/operations | User clicks "Pay" twice             |

### Best Practices

* Use business identifiers (order ID, payment ID) when available
* Follow consistent naming patterns for key generation
* Ensure keys are unique within their scope
* Never reuse idempotency keys for different operations

### Next Steps

<CardGroup cols={2}>
  <Card title="Tracking Workflows" icon="route" href="/core-concepts/tracking-workflows">
    Learn how to record steps in workflows
  </Card>

  <Card title="Handling Failures" icon="triangle-exclamation" href="/core-concepts/handling-failures">
    Handle retries and failures properly
  </Card>
</CardGroup>
