Webhooks
Webhooks allow your application to receive real-time notifications when events happen in your storq.io account. Instead of polling the API, webhooks push data to your server when events occur.
Setting Up Webhooks
Configure webhooks in your dashboard under Settings → Webhooks. Provide a URL endpoint where storq.io will send event notifications.
Webhook Endpoint Requirements
- Must be accessible via HTTPS
- Must respond with a 2xx status code within 5 seconds
- Should process events asynchronously
- Should be idempotent (handle duplicate events)
Event Types
storq.io sends webhooks for the following events:
Product Events
| Event | Description |
|---|---|
product.created |
A new product was created |
product.updated |
A product was updated |
product.deleted |
A product was deleted |
Stock Events
| Event | Description |
|---|---|
stock.received |
Stock was received into a location |
stock.adjusted |
Stock quantity was adjusted |
stock.transferred |
Stock was moved between locations |
stock.low |
Stock level fell below threshold |
Order Events
| Event | Description |
|---|---|
order.created |
A new order was created |
order.picked |
Order picking was completed |
order.packed |
Order packing was completed |
order.shipped |
Order was shipped with tracking |
order.cancelled |
Order was cancelled |
Webhook Payload
All webhook payloads follow this structure:
{
"id": "evt_1234567890",
"type": "order.shipped",
"created": 1642435200,
"data": {
"object": {
"id": 123,
"customer_email": "[email protected]",
"status": "shipped",
"tracking_number": "1Z999AA10123456784",
"shipped_at": "2026-01-16T14:20:00Z"
}
}
}
Verifying Webhook Signatures
Verify that webhooks are sent from storq.io by validating the signature in the Storq-Signature header.
require 'openssl'
def verify_webhook_signature(payload, signature, secret)
expected = OpenSSL::HMAC.hexdigest(
'SHA256',
secret,
payload
)
Rack::Utils.secure_compare(expected, signature)
end
payload = request.body.read
signature = request.headers['Storq-Signature']
secret = ENV['STORQ_WEBHOOK_SECRET']
if verify_webhook_signature(payload, signature, secret)
# Process webhook
else
head :unauthorized
end
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
app.post('/webhooks/storq.io', (req, res) => {
const payload = JSON.stringify(req.body);
const signature = req.headers['storq.io-signature'];
const secret = process.env.STORQ_WEBHOOK_SECRET;
if (verifyWebhookSignature(payload, signature, secret)) {
// Process webhook
res.sendStatus(200);
} else {
res.sendStatus(401);
}
});
import hmac
import hashlib
def verify_webhook_signature(payload, signature, secret):
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# In your webhook endpoint (Flask)
@app.route('/webhooks/storq.io', methods=['POST'])
def handle_webhook():
payload = request.get_data(as_text=True)
signature = request.headers.get('Storq-Signature')
secret = os.environ['STORQ_WEBHOOK_SECRET']
if verify_webhook_signature(payload, signature, secret):
# Process webhook
return '', 200
else:
return '', 401
Example: Processing Order Shipped Events
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
def storq.io
payload = request.body.read
signature = request.headers['Storq-Signature']
unless verify_signature(payload, signature)
return head :unauthorized
end
event = JSON.parse(payload)
case event['type']
when 'order.shipped'
handle_order_shipped(event['data']['object'])
when 'stock.low'
handle_stock_low(event['data']['object'])
end
head :ok
end
private
def handle_order_shipped(order)
customer = Customer.find_by(email: order['customer_email'])
OrderMailer.shipped_notification(
customer,
order['tracking_number']
).deliver_later
end
def verify_signature(payload, signature)
expected = OpenSSL::HMAC.hexdigest(
'SHA256',
ENV['STORQ_WEBHOOK_SECRET'],
payload
)
Rack::Utils.secure_compare(expected, signature)
end
end
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.STORQ_WEBHOOK_SECRET;
app.post('/webhooks/storq.io', (req, res) => {
const signature = req.headers['storq.io-signature'];
const payload = JSON.stringify(req.body);
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
return res.sendStatus(401);
}
const event = req.body;
switch (event.type) {
case 'order.shipped':
handleOrderShipped(event.data.object);
break;
case 'stock.low':
handleStockLow(event.data.object);
break;
}
res.sendStatus(200);
});
async function handleOrderShipped(order) {
await sendEmail({
to: order.customer_email,
template: 'order_shipped',
data: { tracking_number: order.tracking_number }
});
}
import hmac
import hashlib
import json
import os
from flask import Flask, request
app = Flask(__name__)
WEBHOOK_SECRET = os.environ['STORQ_WEBHOOK_SECRET']
@app.route('/webhooks/storq.io', methods=['POST'])
def handle_webhook():
payload = request.get_data(as_text=True)
signature = request.headers.get('Storq-Signature')
expected = hmac.new(
WEBHOOK_SECRET.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
return '', 401
event = json.loads(payload)
if event['type'] == 'order.shipped':
handle_order_shipped(event['data']['object'])
elif event['type'] == 'stock.low':
handle_stock_low(event['data']['object'])
return '', 200
def handle_order_shipped(order):
send_email(
to=order['customer_email'],
template='order_shipped',
data={'tracking_number': order['tracking_number']}
)
Best Practices
- Respond quickly: Return a 200 status immediately, then process the event asynchronously
- Handle duplicates: Events may be delivered more than once. Use the event
idto track processed events - Verify signatures: Always verify webhook signatures to ensure authenticity
- Monitor failures: Set up alerts for webhook endpoint failures
- Use retries: storq.io will retry failed webhooks up to 3 times with exponential backoff
Testing Webhooks
Use the webhook testing tool in your dashboard to send test events to your endpoint.
Local development: Use tools like ngrok or LocalTunnel to expose your local server for webhook testing.