Skip to main content
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:
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');
});
@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:
StorageProsCons
RedisFast, built-in TTLRequires Redis instance
DatabaseAlready availableSlower, needs cleanup job
In-memorySimplestLost on restart, not for distributed systems
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.
Even without deduplication, designing your webhook handlers to be idempotent (producing the same result when called multiple times) is a good practice.