Webhooks

Receive real-time notifications when events occur in your store. Subscribe to product changes, order updates, customer activity, and more.

How Webhooks Work

  1. Subscribe — Register a webhook URL and specify which events you want to receive
  2. Event Occurs — When something happens (order placed, product updated, etc.), BareCommerce prepares a payload
  3. Delivery — We POST the payload to your URL with a signature for verification
  4. Process — Your server verifies the signature and processes the event

Endpoints

MethodEndpointDescription
GET/webhooksList webhook subscriptions
POST/webhooksCreate a subscription
GET/webhooks/:idGet a subscription
PUT/webhooks/:idUpdate a subscription
DELETE/webhooks/:idDelete 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.created
  • product.updated
  • product.deleted

Orders

  • order.created
  • order.updated

Customers

  • customer.created
  • customer.updated
  • customer.deleted

Categories

  • category.created
  • category.updated
  • category.deleted

Pages

  • page.created
  • page.updated
  • page.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

FieldTypeDescription
urlstringHTTPS endpoint to receive events (required)
eventsstring[]Events to subscribe to (at least one required)
secretstringSecret for signature verification (min 16 chars)
isActivebooleanEnable/disable the webhook (default: true)
lastTriggeredAtstring?Last successful delivery timestamp

Create Webhook

POST /stores/{storeId}/webhooks

Example: 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

HeaderDescription
X-Webhook-SignatureHMAC-SHA256 signature for verification
X-Webhook-EventEvent type (e.g., "order.created")
X-Webhook-TimestampISO timestamp of the event
Content-Typeapplication/json
User-AgentBareCommerce-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', 200

PHP

<?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

BehaviorDetails
TimeoutRequests timeout after 10 seconds
SuccessAny 2xx status code is considered successful
ParallelMultiple webhooks are delivered in parallel
RetriesNo 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

  1. Always verify signatures — Never trust payloads without cryptographic verification
  2. Respond quickly (within 10s) — Acknowledge receipt immediately, process asynchronously
  3. Handle duplicate events — Make handlers idempotent; events may be delivered more than once
  4. Use separate webhooks per environment — Different subscriptions for dev, staging, production
  5. Store the raw payload — Log full payloads before processing for debugging and replay

Related Guides

  • Orders — Understand order events and lifecycle
  • Products — Product events for catalog sync