Webhooks

Receive real-time HTTP callbacks whenever important events occur in your SpireStock workspace — new orders, status changes, user sign-ups, and payment confirmations.

How Webhooks Work

Webhooks follow a simple four-step flow:

  1. Register— You configure an HTTPS endpoint URL and choose the events you want to subscribe to via the SpireStock dashboard or API.
  2. Trigger— An event occurs in your workspace (e.g., a new order is created).
  3. Deliver — SpireStock sends a POST request containing the event payload to your registered endpoint within seconds.
  4. Acknowledge — Your server responds with a 2xx status code. If delivery fails, SpireStock retries with exponential back-off for up to 72 hours.
💡

HTTPS Required

Webhook endpoints must use HTTPS. Plain HTTP URLs are rejected to protect payload integrity and confidentiality.

Event Types

Subscribe to one or more of the following event types:

EventDescriptionFired When
order.createdA new order has been placedA sales rep or customer submits a new order through any channel
order.deliveredAn order has been deliveredThe order status changes to delivered (status 4)
user.createdA new user joined the workspaceAn admin creates a new user or a user signs up in your workspace

Payload Format

Every webhook delivery includes a set of custom headers and a JSON body.

Headers

HeaderDescriptionExample
X-Webhook-EventThe event type that triggered this deliveryorder.created
X-Webhook-SignatureHMAC-SHA256 hex digest for payload verificationsha256=a1b2c3d4e5...
X-Webhook-TimestampUnix timestamp (seconds) when the event was sent1717200000
Content-TypeAlways JSONapplication/json

Example Payload

Below is a sample payload for an order.created event:

order.created payload
{
  "id": 1,
  "event": "order.created",
  "timestamp": "2026-06-01T10:30:00.000Z",
  "organization_id": 5,
  "data": {
    "id": 142,
    "order_code": "ORD-2026-0142",
    "order_status": 1,
    "user_id": 42,
    "user_name": "Sharma Dairy Store",
    "order_total_amount": 1400.00,
    "order_items_count": 1,
    "order_date": "2026-06-01",
    "createdAt": "2026-06-01T10:30:00.000Z"
  }
}

Signature Verification

Every webhook delivery is signed using your workspace’s webhook secret. You should always verify the signature before processing a payload to ensure it was sent by SpireStock and has not been tampered with.

How It Works

  1. Concatenate the timestamp and the raw request body, separated by a dot: {timestamp}.{body}
  2. Compute an HMAC-SHA256 digest using your webhook secret as the key.
  3. Compare the computed signature with the value in the X-Webhook-Signature header using a timing-safe comparison to prevent timing attacks.
🚨

Never skip verification

Without signature verification, an attacker could forge webhook deliveries to your endpoint. Always use a timing-safe comparison function to prevent timing-based side-channel attacks.

Signature Verification
import crypto from "crypto";
import express from "express";

const app = express();

// Use raw body for signature verification
app.post(
  "/webhooks/spirestock",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.headers["x-webhook-signature"];
    const timestamp = req.headers["x-webhook-timestamp"];
    const body = req.body.toString();

    // Build the signed content
    const signedContent = `${timestamp}.${body}`;

    // Compute expected signature
    const expected = "sha256=" +
      crypto
        .createHmac("sha256", process.env.WEBHOOK_SECRET)
        .update(signedContent)
        .digest("hex");

    // Timing-safe comparison
    const isValid =
      expected.length === signature.length &&
      crypto.timingSafeEqual(
        Buffer.from(expected),
        Buffer.from(signature)
      );

    if (!isValid) {
      return res.status(401).json({ error: "Invalid signature" });
    }

    // Parse and process the event
    const event = JSON.parse(body);
    console.log("Received event:", event.event, event.id);

    // Acknowledge receipt
    res.status(200).json({ received: true });
  }
);

app.listen(3000);

Best Practices

  • Respond quickly — Return a 2xx status within 5 seconds. Offload heavy processing to a background queue.
  • Handle duplicates — Use the event id field to deduplicate deliveries. SpireStock may retry events that did not receive an acknowledgment.
  • Validate timestamps— Reject events where the timestamp is more than 5 minutes old to guard against replay attacks.
  • Use a secret per environment— Generate unique webhook secrets for staging and production to isolate environments.
  • Monitor failures— Check the webhook delivery logs in the SpireStock dashboard for failed deliveries and error details.
  • Consider IP allowlisting— For additional defense-in-depth, restrict your webhook endpoint to only accept requests from SpireStock’s IP ranges.