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

# Idempotency & Deduplication

> Handle duplicate webhook deliveries safely using the svix-id header. Deduplicate events and design idempotent handlers for at-least-once delivery.

storekit webhooks are delivered with "at least once" semantics. This means if there are issues during delivery (e.g., network problems), a webhook may occasionally be delivered more than once.

## The svix-id Header

Every webhook request includes a `svix-id` header. This ID is:

* **Unique per message** - Each distinct event gets a unique ID
* **Consistent across retries** - The same ID is used when retrying a failed delivery

```
svix-id: msg_2KWPBCMzR5VXYW8xqGDKd0SLnHk
```

## Implementing Deduplication

To ensure you only process each event once, store the `svix-id` and check it before processing:

```javascript theme={null}
app.post('/webhooks', async (req, res) => {
  const svixId = req.headers['svix-id'];
  
  // Check if we've already processed this webhook
  const alreadyProcessed = await redis.get(`webhook:${svixId}`);
  if (alreadyProcessed) {
    return res.status(200).send('Already processed');
  }
  
  // Mark as processed (with 72-hour expiry)
  await redis.set(`webhook:${svixId}`, '1', 'EX', 259200);
  
  // Process the webhook
  await processWebhook(req.body);
  
  res.status(200).send('OK');
});
```

```python theme={null}
@app.route('/webhooks', methods=['POST'])
def handle_webhook():
    svix_id = request.headers.get('svix-id')
    
    # Check if already processed
    if redis_client.get(f'webhook:{svix_id}'):
        return 'Already processed', 200
    
    # Mark as processed (72-hour expiry)
    redis_client.setex(f'webhook:{svix_id}', 259200, '1')
    
    # Process the webhook
    process_webhook(request.json)
    
    return 'OK', 200
```

## When to Use Deduplication

Deduplication is especially important for:

* **Payment processing** - Avoid charging customers twice
* **Order creation** - Prevent duplicate orders
* **Inventory updates** - Ensure accurate stock counts
* **Notification sending** - Don't spam users with duplicate messages

## Storage Options

You can store processed webhook IDs in:

| Storage   | Pros               | Cons                                         |
| --------- | ------------------ | -------------------------------------------- |
| Redis     | Fast, built-in TTL | Requires Redis instance                      |
| Database  | Already available  | Slower, needs cleanup job                    |
| In-memory | Simplest           | Lost on restart, not for distributed systems |

<Tip>
  Set an expiry of at least 72 hours (3 days) on stored webhook IDs. Retries can occur over approximately 3 days according to the [retry schedule](/developers/webhooks/retry-policy).
</Tip>

<Info>
  Even without deduplication, designing your webhook handlers to be idempotent (producing the same result when called multiple times) is a good practice.
</Info>
