Skip to main content

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.

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.