Skip to main content
POST
/
api
/
v1
/
webhooks
Webhook Verification
curl --request POST \
  --url https://api.launchmystore.io/api/v1/webhooks \
  --header 'Authorization: Bearer <token>'

Documentation Index

Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt

Use this file to discover all available pages before exploring further.

Webhook Verification

All webhooks from LaunchMyStore are signed using HMAC-SHA256. You should verify this signature to ensure the webhook payload is authentic and hasn’t been tampered with.

How It Works

  1. LaunchMyStore computes an HMAC-SHA256 signature of the request body using your app’s client secret
  2. The signature is included in the X-LMS-Hmac-SHA256 header
  3. Your app computes the same signature and compares it to verify authenticity

Webhook Headers

Each webhook request includes these headers:
HeaderDescription
X-LMS-Hmac-SHA256Base64-encoded HMAC-SHA256 signature
X-LMS-TopicThe webhook topic (e.g., orders/create)
X-LMS-Shop-DomainThe store’s domain slug
X-LMS-API-VersionAPI version used for the payload
X-LMS-Webhook-IdUnique ID for this webhook delivery
X-LMS-Triggered-AtISO 8601 timestamp when the webhook was triggered

Verification Examples

const crypto = require('crypto');

function verifyWebhook(req, clientSecret) {
  const hmacHeader = req.headers['x-lms-hmac-sha256'];
  
  // Get raw request body (must be the raw string, not parsed JSON)
  const body = req.rawBody;
  
  // Compute HMAC
  const computedHmac = crypto
    .createHmac('sha256', clientSecret)
    .update(body, 'utf8')
    .digest('base64');
  
  // Compare using timing-safe comparison
  return crypto.timingSafeEqual(
    Buffer.from(hmacHeader),
    Buffer.from(computedHmac)
  );
}

// Express middleware example
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  const isValid = verifyWebhook(req, process.env.CLIENT_SECRET);
  
  if (!isValid) {
    console.error('Invalid webhook signature');
    return res.status(401).send('Unauthorized');
  }
  
  // Process the webhook
  const payload = JSON.parse(req.body);
  console.log('Received webhook:', req.headers['x-lms-topic']);
  
  res.status(200).send('OK');
});

Important Considerations

Use Raw Request Body

The signature is computed over the raw request body. If you parse the JSON before verifying, the signature check will fail due to potential whitespace or ordering differences.

Timing-Safe Comparison

Always use timing-safe string comparison functions to prevent timing attacks:
  • Node.js: crypto.timingSafeEqual()
  • Python: hmac.compare_digest()
  • PHP: hash_equals()
  • Ruby: Rack::Utils.secure_compare()
  • Go: hmac.Equal()

Handle Retries

If your endpoint returns a non-2xx status code, the webhook will be retried. Implement idempotency using the X-LMS-Webhook-Id header to avoid processing the same event twice.
const processedWebhooks = new Set();

app.post('/webhooks', (req, res) => {
  const webhookId = req.headers['x-lms-webhook-id'];
  
  if (processedWebhooks.has(webhookId)) {
    // Already processed, return success
    return res.status(200).send('OK');
  }
  
  // Process webhook...
  
  processedWebhooks.add(webhookId);
  res.status(200).send('OK');
});

Testing Webhooks

Use the webhook test endpoint to trigger a test delivery:
curl -X POST "https://api.launchmystore.io/api/v1/webhooks/webhook_abc123/test" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
This sends a sample payload to your registered endpoint so you can verify your integration.