Back to Developer

Webhooks

Receive real-time event notifications when things happen in your store.

How It Works

Configure a webhook endpoint with a URL and a signing secret. When events occur, WhaleTools sends a POST request to your endpoint with the event payload signed with HMAC-SHA256.

  1. 1.Create a webhook endpoint via the API or dashboard.
  2. 2.Store the signing secret securely.
  3. 3.WhaleTools POSTs events to your URL as they happen.
  4. 4.Your server verifies the signature and processes the event.

Register a Webhook

Create an outbound webhook endpoint via the API. The response includes a signing_secret you'll use to verify payloads.

curl -X POST https://whale-gateway.fly.dev/v1/stores/{store_id}/webhooks \
  -H "x-api-key: wk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/whaletools",
    "events": ["order.created", "order.updated", "product.created"],
    "description": "My app webhook"
  }'

// Response
{
  "object": "outbound_webhook_endpoint",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "url": "https://your-app.com/webhooks/whaletools",
  "events": ["order.created", "order.updated", "product.created"],
  "signing_secret": "a1b2c3d4e5f6...",
  "is_active": true,
  "total_delivered": 0,
  "total_failed": 0
}

Payload Format

POST https://your-app.com/webhooks/whaletools
Content-Type: application/json
X-Webhook-Signature: sha256=a1b2c3...
X-Webhook-Timestamp: 1741513200
X-Webhook-Event: order.created
X-Webhook-Delivery-Id: 7f3a9c2e-...
User-Agent: WhaleTools-Webhook/1.0

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "type": "order.created",
  "created_at": "2026-03-09T12:00:00.000Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "object": "order",
    "order_number": "#10042",
    "status": "completed",
    "payment_method": "card"
  }
}

Signature Verification

Always verify the HMAC-SHA256 signature to ensure the webhook came from WhaleTools. The signature covers the raw request body and is compared using timing-safe equality.

import crypto from 'crypto';

function verifyWebhook(body: string, signature: string, secret: string): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expected}`)
  );
}

// Express handler
app.post('/webhooks/whaletools', (req, res) => {
  const sig = req.headers['x-webhook-signature'] as string;
  const raw = JSON.stringify(req.body);

  if (!verifyWebhook(raw, sig, process.env.WEBHOOK_SECRET!)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Check timestamp freshness (< 5 minutes)
  const age = Date.now() / 1000 - req.body.timestamp;
  if (age > 300) {
    return res.status(401).json({ error: 'Timestamp too old' });
  }

  // Process the event
  console.log('Event:', req.body.event, req.body.data);
  res.json({ received: true });
});

Event Types

EventDescription
order.createdA new order was placed.
order.updatedOrder status, payment, or fulfillment changed.
order.paidPayment confirmed for an order.
order.fulfilledOrder has been shipped or delivered.
order.cancelledOrder was cancelled.
product.createdNew product added to catalog.
product.updatedProduct details changed.
product.deletedProduct archived or deleted.
customer.createdNew customer registered.
customer.updatedCustomer profile changed.
inventory.adjustedStock level adjusted.
inventory.transferredStock transferred between locations.
checkout.completedCheckout finalized into order.
agent.messageAI agent sent or received a message.

Best Practices

  • Return 200 quickly. Process events asynchronously. WhaleTools considers non-2xx responses as failures and will retry.
  • Handle duplicates. Use the event ID for idempotency. The same event may be delivered more than once.
  • Verify signatures. Never trust unverified payloads. Always check the HMAC signature and timestamp.
  • Use HTTPS. Webhook endpoints must use TLS. HTTP URLs will be rejected.
  • Retry policy. Failed deliveries are retried up to 5 times with exponential backoff (30s, 2m, 10m, 1h, 6h). Check the delivery log via GET /v1/stores/:storeId/webhooks/:id/deliveries.
  • Test your endpoint. Use POST /v1/stores/:storeId/webhooks/:id/test to send a test ping before going live.