Webhooks
Receive real-time notifications when events occur in your store. Subscribe to product changes, order updates, customer activity, and more.
How Webhooks Work
- Subscribe — Register a webhook URL and specify which events you want to receive
- Event Occurs — When something happens (order placed, product updated, etc.), BareCommerce prepares a payload
- Delivery — We POST the payload to your URL with a signature for verification
- Process — Your server verifies the signature and processes the event
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /webhooks | List webhook subscriptions |
| POST | /webhooks | Create a subscription |
| GET | /webhooks/:id | Get a subscription |
| PUT | /webhooks/:id | Update a subscription |
| DELETE | /webhooks/:id | Delete a subscription |
All endpoints are prefixed with /stores/{storeId}.
Available Events
Subscribe to specific events or use wildcards (e.g., product.*) to receive all events for a resource.
Products
product.createdproduct.updatedproduct.deleted
Orders
order.createdorder.updated
Customers
customer.createdcustomer.updatedcustomer.deleted
Categories
category.createdcategory.updatedcategory.deleted
Pages
page.createdpage.updatedpage.deleted
The Webhook Object
{
"id": "wh_abc123",
"storeId": "store_xyz789",
"url": "https://example.com/webhooks/barecommerce",
"events": ["order.created", "order.updated"],
"secret": "whsec_your_secret_key_here",
"isActive": true,
"lastTriggeredAt": "2024-01-15T14:30:00.000Z",
"createdAt": "2024-01-10T08:00:00.000Z",
"updatedAt": "2024-01-10T08:00:00.000Z"
}Fields
| Field | Type | Description |
|---|---|---|
| url | string | HTTPS endpoint to receive events (required) |
| events | string[] | Events to subscribe to (at least one required) |
| secret | string | Secret for signature verification (min 16 chars) |
| isActive | boolean | Enable/disable the webhook (default: true) |
| lastTriggeredAt | string? | Last successful delivery timestamp |
Create Webhook
POST /stores/{storeId}/webhooksExample: Subscribe to Order Events
curl -X POST "https://api.barecommercecore.com/stores/{storeId}/webhooks" \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/barecommerce",
"events": ["order.*"],
"secret": "whsec_your_secret_minimum_16_chars"
}'ℹ️ Wildcards Expand Automatically: Using
order.*subscribes to all order events. The response shows the expanded list.
Webhook Payload
When an event occurs, BareCommerce sends an HTTP POST request to your webhook URL.
Request Headers
| Header | Description |
|---|---|
| X-Webhook-Signature | HMAC-SHA256 signature for verification |
| X-Webhook-Event | Event type (e.g., "order.created") |
| X-Webhook-Timestamp | ISO timestamp of the event |
| Content-Type | application/json |
| User-Agent | BareCommerce-Webhook/1.0 |
Payload Body
{
"event": "order.created",
"timestamp": "2024-01-15T14:30:00.000Z",
"storeId": "store_xyz789",
"data": {
"id": "order_abc123",
"orderNumber": "ORD-1001",
"status": "pending",
"total": "149.99",
"customerId": "cust_def456",
"items": [...],
"shippingAddress": {...},
"createdAt": "2024-01-15T14:30:00.000Z"
}
}Signature Verification
⚠️ Security Warning: Never process webhook payloads without verifying the signature. An attacker could forge requests to your endpoint.
Node.js
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Express.js example
app.post('/webhooks/barecommerce', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = req.body.toString();
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
switch (event.event) {
case 'order.created':
handleNewOrder(event.data);
break;
case 'product.updated':
syncProductToSearch(event.data);
break;
}
res.status(200).send('OK');
});Python
import hmac
import hashlib
from flask import Flask, request, abort
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
@app.route('/webhooks/barecommerce', methods=['POST'])
def webhook_handler():
signature = request.headers.get('X-Webhook-Signature')
if not signature or not verify_signature(request.data, signature, WEBHOOK_SECRET):
abort(401)
event = request.json
# Process event...
return 'OK', 200PHP
<?php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'];
$expectedSignature = 'sha256=' . hash_hmac('sha256', $payload, $webhookSecret);
if (!hash_equals($expectedSignature, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
// Process event...Delivery Behavior
| Behavior | Details |
|---|---|
| Timeout | Requests timeout after 10 seconds |
| Success | Any 2xx status code is considered successful |
| Parallel | Multiple webhooks are delivered in parallel |
| Retries | No automatic retries (coming soon) |
Update Webhook
PUT /stores/{storeId}/webhooks/{webhookId}Example: Disable Webhook
curl -X PUT "https://api.barecommercecore.com/stores/{storeId}/webhooks/wh_abc123" \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "isActive": false }'Delete Webhook
DELETE /stores/{storeId}/webhooks/{webhookId}Returns 204 No Content.
Common Use Cases
Sync Products to Search Engine
// Events: ["product.created", "product.updated", "product.deleted"]
if (event.event === 'product.deleted') {
await searchIndex.deleteObject(event.data.id);
} else {
await searchIndex.saveObject({
objectID: event.data.id,
title: event.data.title,
price: event.data.price,
});
}Send Order Notifications
// Events: ["order.created"]
if (event.event === 'order.created') {
await sendEmail(order.customerId, 'order_confirmation', { order });
await slack.send(`New order #${order.orderNumber}: $${order.total}`);
}Inventory Alerts
// Events: ["product.updated"]
if (event.data.stock <= 5 && event.data.stock > 0) {
await sendAlert(`Low stock: ${event.data.title}`);
} else if (event.data.stock === 0) {
await sendAlert(`Out of stock: ${event.data.title}`);
}Error Responses
400 Invalid Event
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{ "field": "events", "reason": "Invalid event: order.shipped" }
]
}
}400 HTTP URL (Not HTTPS)
{
"error": {
"code": "VALIDATION_ERROR",
"details": [
{ "field": "url", "reason": "Webhook URL must use HTTPS" }
]
}
}409 Duplicate URL
{
"error": {
"code": "CONFLICT",
"message": "Webhook with this URL already exists"
}
}Best Practices
- Always verify signatures — Never trust payloads without cryptographic verification
- Respond quickly (within 10s) — Acknowledge receipt immediately, process asynchronously
- Handle duplicate events — Make handlers idempotent; events may be delivered more than once
- Use separate webhooks per environment — Different subscriptions for dev, staging, production
- Store the raw payload — Log full payloads before processing for debugging and replay