Skip to content

Webhooks BETA

Webhooks provide real-time notifications when specific events occur in our system. No more polling required!

⚠️ Beta Feature: The webhook functionality is currently in beta. While fully functional, the API and behavior may change in future releases.

Overview

Webhooks allow you to:

  • 📦 Monitor product stock changes
  • 💰 Track price updates
  • 🔄 Receive real-time notifications
  • 🔐 Secure payload verification

Available Events

EventDescriptionTrigger
productOutOfStockProduct stock reaches zeroStock: >0 → 0
productBackInStockProduct becomes availableStock: 0 → >0
productPriceChangedProduct price updatedAny price change

Webhook Management

Get All Webhooks

GET/api/webhooks
json
{
  "betaNotice": "The webhook functionality is currently in beta...",
  "webhooks": [
    {
      "_id": "webhook_id_1",
      "customerId": "customer_id",
      "url": "https://your-domain.com/webhook",
      "events": {
        "productOutOfStock": true,
        "productBackInStock": true,
        "productPriceChanged": false
      },
      "active": true,
      "createdAt": "2023-06-15T12:00:00Z",
      "updatedAt": "2023-06-15T12:00:00Z"
    }
  ]
}
bash
curl -X GET https://api.attivita.de/api/webhooks \
  -H "Authorization: Bearer YOUR_API_ACCESS_TOKEN"

Create Webhook

POST/api/webhooks
json
{
  "url": "https://your-domain.com/webhook",
  "events": {
    "productOutOfStock": true,
    "productBackInStock": true,
    "productPriceChanged": false
  }
}
json
{
  "betaNotice": "The webhook functionality is currently in beta...",
  "webhook": {
    "_id": "webhook_id",
    "customerId": "customer_id",
    "url": "https://your-domain.com/webhook",
    "events": {
      "productOutOfStock": true,
      "productBackInStock": true,
      "productPriceChanged": false
    },
    "active": true,
    "secret": "whsec_1234567890abcdef",
    "createdAt": "2023-06-15T12:00:00Z",
    "updatedAt": "2023-06-15T12:00:00Z"
  }
}

Important

Store the secret securely! It's only shown once and is required for signature verification.

Update Webhook

PATCH/api/webhooks/:id
json
{
  "url": "https://new-domain.com/webhook",
  "events": {
    "productOutOfStock": true,
    "productBackInStock": true,
    "productPriceChanged": true
  },
  "active": true
}

Delete Webhook

DELETE/api/webhooks/:id

Regenerate Secret

POST/api/webhooks/:id/regenerate-secret

Use this endpoint if you believe your webhook secret has been compromised.

Event Payloads

Product Out of Stock

json
{
  "event": "productOutOfStock",
  "timestamp": 1623761345123,
  "data": {
    "productId": "507f1f77bcf86cd799439011",
    "name": "Microsoft Office 2021",
    "sku": "MS-OFF-2021"
  }
}

Product Back in Stock

json
{
  "event": "productBackInStock",
  "timestamp": 1623761345123,
  "data": {
    "productId": "507f1f77bcf86cd799439011",
    "name": "Microsoft Office 2021",
    "sku": "MS-OFF-2021",
    "availableStock": 50
  }
}

Product Price Changed

json
{
  "event": "productPriceChanged",
  "timestamp": 1623761345123,
  "data": {
    "productId": "507f1f77bcf86cd799439011",
    "name": "Microsoft Office 2021",
    "sku": "MS-OFF-2021",
    "oldPrice": 249.99,
    "newPrice": 199.99
  }
}

Security & Verification

Every webhook request includes a signature in the X-Webhook-Signature header. Always verify this signature!

Signature Verification

javascript
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const computedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(computedSignature)
  );
}

// Express.js handler
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const payload = JSON.parse(req.body);
  
  if (!signature) {
    return res.status(401).send('No signature');
  }
  
  if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process webhook
  console.log('Event:', payload.event);
  console.log('Data:', payload.data);
  
  res.status(200).send('OK');
});
python
import hmac
import hashlib
import json

def verify_webhook_signature(payload, signature, secret):
    computed_signature = hmac.new(
        secret.encode(),
        json.dumps(payload).encode(),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, computed_signature)

# Flask handler
@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Webhook-Signature')
    
    if not signature:
        return 'No signature', 401
    
    payload = request.get_json()
    
    if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
        return 'Invalid signature', 401
    
    # Process webhook
    print(f"Event: {payload['event']}")
    print(f"Data: {payload['data']}")
    
    return 'OK', 200
php
function verifyWebhookSignature($payload, $signature, $secret) {
    $computedSignature = hash_hmac(
        'sha256',
        json_encode($payload),
        $secret
    );
    
    return hash_equals($signature, $computedSignature);
}

// Handler
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$payload = json_decode(file_get_contents('php://input'), true);

if (!$signature) {
    http_response_code(401);
    exit('No signature');
}

if (!verifyWebhookSignature($payload, $signature, $_ENV['WEBHOOK_SECRET'])) {
    http_response_code(401);
    exit('Invalid signature');
}

// Process webhook
error_log('Event: ' . $payload['event']);
error_log('Data: ' . print_r($payload['data'], true));

http_response_code(200);
echo 'OK';

Best Practices

1. Respond Quickly

  • Acknowledge receipt within 5 seconds
  • Process webhooks asynchronously
  • Return 2xx status code immediately

2. Handle Failures Gracefully

  • Implement idempotency
  • Handle duplicate deliveries
  • Log all webhook events

3. Security First

  • Always verify signatures
  • Use HTTPS endpoints only
  • Validate payload structure

4. Monitor & Debug

  • Log all webhook activity
  • Monitor endpoint availability
  • Set up alerting for failures

Testing Webhooks

Local Development

Use tools like ngrok to expose your local webhook endpoint:

bash
# Start your local server
npm run dev

# In another terminal
ngrok http 3000

# Use the ngrok URL for webhook registration
https://abc123.ngrok.io/webhook

Webhook Testing Tools

Troubleshooting

Common Issues

IssueSolution
No webhooks receivedCheck if webhook is active and URL is correct
401 UnauthorizedVerify signature implementation
Timeout errorsRespond within 5 seconds
Duplicate eventsImplement idempotency

Webhook Delivery

  • Maximum retry attempts: 3
  • Retry delays: 10s, 30s, 60s
  • Webhook disabled after 10 consecutive failures

Next Steps

The usage of this API is at your own risk. Attivita GmbH is not responsible for any damages or losses.