> ## Documentation Index
> Fetch the complete documentation index at: https://quickbutik.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Get real-time notifications when events occur in your Quickbutik store

Webhooks allow your application to receive real-time notifications when important events happen in your Quickbutik store. Instead of constantly polling the API, webhooks push data to your application immediately when events occur.

<Info>
  **Common use cases:**

  * Sync new orders to your fulfillment system instantly
  * Update inventory levels across multiple platforms
  * Send custom order confirmations or notifications
  * Trigger automated workflows and integrations
</Info>

## How Webhooks Work

When an event occurs in your store (like a new order), Quickbutik sends an HTTP GET request to your configured webhook URL with event details as query parameters.

```mermaid theme={null}
sequenceDiagram
    participant QS as Quickbutik Store
    participant QW as Quickbutik Webhooks
    participant YE as Your Endpoint
    participant YS as Your System

    QS->>QW: Order Created
    QW->>YE: GET /webhook?event_type=order.new&order_id=12345
    YE->>YE: Acknowledge (200 OK)
    YE->>YS: Process Order Async
    YS->>QS: Fetch Order Details (API)
```

## Quick Setup

<Steps>
  <Step title="Create webhook endpoint">
    Set up an endpoint in your application to receive webhook notifications

    ```javascript theme={null}
    app.get('/webhooks/quickbutik', (req, res) => {
      const { event_type, order_id, product_id } = req.query;
      
      // Always respond quickly
      res.status(200).send('OK');
      
      // Process webhook asynchronously
      processWebhook(event_type, { order_id, product_id });
    });
    ```
  </Step>

  <Step title="Configure in Quickbutik">
    Enable webhooks in your Quickbutik Control Panel under **Settings → Webhooks** and add your endpoint URL
  </Step>

  <Step title="Handle events">
    Process the webhook events in your application based on the event type
  </Step>
</Steps>

## Event Structure

All webhook requests are sent as GET requests with the following query parameters:

| Parameter    | Description                     | Example                       |
| ------------ | ------------------------------- | ----------------------------- |
| `event_type` | The type of event that occurred | `order.new`, `product.update` |
| `order_id`   | Order ID (for order events)     | `12345`                       |
| `product_id` | Product ID (for product events) | `67890`                       |

### Example Webhook Request

```
GET /your-webhook-endpoint?event_type=order.new&order_id=12345
Host: your-domain.com
User-Agent: Quickbutik-Webhooks/1.0
```

## Complete Example

Here's a complete webhook handler that processes different event types:

<CodeGroup>
  ```javascript Express.js theme={null}
  const express = require('express');
  const QuickbutikAPI = require('./quickbutik-api');

  const app = express();
  const api = new QuickbutikAPI(process.env.QUICKBUTIK_API_KEY);

  app.get('/webhooks/quickbutik', async (req, res) => {
    const { event_type, order_id, product_id } = req.query;
    
    console.log(`Received webhook: ${event_type}`, { order_id, product_id });
    
    // Acknowledge webhook immediately (important!)
    res.status(200).send('OK');
    
    try {
      // Process webhook asynchronously
      switch (event_type) {
        case 'order.new':
          await handleNewOrder(order_id);
          break;
        case 'order.done':
          await handleOrderShipped(order_id);
          break;
        case 'order.cancelled':
          await handleOrderCancelled(order_id);
          break;
        case 'product.add':
          await handleProductAdded(product_id);
          break;
        case 'product.update':
          await handleProductUpdated(product_id);
          break;
        case 'product.delete':
          await handleProductDeleted(product_id);
          break;
        default:
          console.log(`Unhandled event type: ${event_type}`);
      }
    } catch (error) {
      console.error(`Error processing webhook ${event_type}:`, error);
      
      // In production, add to retry queue or send alert
      await handleWebhookError(event_type, { order_id, product_id }, error);
    }
  });

  async function handleNewOrder(orderId) {
    console.log(`Processing new order: ${orderId}`);
    
    // Fetch complete order details
    const orders = await api.getOrders({ 
      order_id: orderId,
      include_details: true 
    });
    
    if (orders && orders.length > 0) {
      const order = orders[0];
      
      // Your business logic here
      await processNewOrder(order);
      console.log(`Successfully processed order ${orderId}`);
    }
  }

  async function handleProductUpdated(productId) {
    console.log(`Processing product update: ${productId}`);
    
    // Fetch updated product details
    const products = await api.getProducts({ 
      product_id: productId,
      include_details: true 
    });
    
    if (products && products.length > 0) {
      const product = products[0];
      
      // Your business logic here
      await syncProductToExternalSystem(product);
      console.log(`Successfully synced product ${productId}`);
    }
  }

  app.listen(3000, () => {
    console.log('Webhook server running on port 3000');
  });
  ```

  ```python Flask theme={null}
  from flask import Flask, request, jsonify
  import asyncio
  import logging
  from quickbutik_api import QuickbutikAPI

  app = Flask(__name__)
  logging.basicConfig(level=logging.INFO)

  api = QuickbutikAPI(os.getenv('QUICKBUTIK_API_KEY'))

  @app.route('/webhooks/quickbutik', methods=['GET'])
  def quickbutik_webhook():
      event_type = request.args.get('event_type')
      order_id = request.args.get('order_id')
      product_id = request.args.get('product_id')
      
      app.logger.info(f'Received webhook: {event_type}, order_id: {order_id}, product_id: {product_id}')
      
      # Acknowledge webhook immediately (important!)
      response = jsonify({'status': 'received'})
      
      # Process webhook asynchronously
      try:
          if event_type == 'order.new':
              asyncio.create_task(handle_new_order(order_id))
          elif event_type == 'order.done':
              asyncio.create_task(handle_order_shipped(order_id))
          elif event_type == 'order.cancelled':
              asyncio.create_task(handle_order_cancelled(order_id))
          elif event_type == 'product.add':
              asyncio.create_task(handle_product_added(product_id))
          elif event_type == 'product.update':
              asyncio.create_task(handle_product_updated(product_id))
          elif event_type == 'product.delete':
              asyncio.create_task(handle_product_deleted(product_id))
          else:
              app.logger.info(f'Unhandled event type: {event_type}')
      except Exception as error:
          app.logger.error(f'Error processing webhook {event_type}: {error}')
          asyncio.create_task(handle_webhook_error(event_type, {'order_id': order_id, 'product_id': product_id}, error))
      
      return response, 200

  async def handle_new_order(order_id):
      app.logger.info(f'Processing new order: {order_id}')
      
      try:
          # Fetch complete order details
          orders = await api.get_orders(order_id=order_id, include_details=True)
          
          if orders:
              order = orders[0]
              
              # Your business logic here
              await process_new_order(order)
              app.logger.info(f'Successfully processed order {order_id}')
      except Exception as error:
          app.logger.error(f'Failed to process order {order_id}: {error}')

  async def handle_product_updated(product_id):
      app.logger.info(f'Processing product update: {product_id}')
      
      try:
          # Fetch updated product details
          products = await api.get_products(product_id=product_id, include_details=True)
          
          if products:
              product = products[0]
              
              # Your business logic here
              await sync_product_to_external_system(product)
              app.logger.info(f'Successfully synced product {product_id}')
      except Exception as error:
          app.logger.error(f'Failed to sync product {product_id}: {error}')

  if __name__ == '__main__':
      app.run(debug=True, port=3000)
  ```
</CodeGroup>

## Best Practices

<CardGroup cols={2}>
  <Card title="Respond Quickly" icon="clock">
    **Always respond with 200 OK within 10 seconds** to acknowledge receipt. Process the actual work asynchronously.
  </Card>

  <Card title="Handle Retries" icon="arrows-rotate">
    **Webhooks may be sent multiple times** if your endpoint doesn't respond. Make your processing idempotent.
  </Card>

  <Card title="Validate Events" icon="shield-check">
    **Verify webhook authenticity** in production environments using request validation.
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation">
    **Implement proper error handling** and retry mechanisms for failed webhook processing.
  </Card>
</CardGroup>

<Warning>
  **Important Considerations:**

  * Webhooks are sent as GET requests (not POST)
  * Always acknowledge webhooks quickly to avoid retries
  * Events may be delivered more than once - ensure idempotent processing
  * Use the API to fetch complete data when processing webhooks
</Warning>

## Testing Webhooks

Use tools like ngrok to test webhooks locally:

<CodeGroup>
  ```bash Terminal theme={null}
  # Install ngrok
  npm install -g ngrok

  # Start your webhook server
  node webhook-server.js

  # In another terminal, expose your local server
  ngrok http 3000

  # Use the ngrok URL in your Quickbutik webhook settings
  # Example: https://abc123.ngrok.io/webhooks/quickbutik
  ```

  ```javascript Testing Script theme={null}
  // Simple webhook tester
  const express = require('express');
  const app = express();

  app.get('/webhooks/quickbutik', (req, res) => {
    console.log('Webhook received:', {
      event_type: req.query.event_type,
      order_id: req.query.order_id,
      product_id: req.query.product_id,
      timestamp: new Date().toISOString()
    });
    
    res.status(200).send('Webhook received successfully');
  });

  app.listen(3000, () => {
    console.log('Webhook test server running on http://localhost:3000');
    console.log('Test URL: http://localhost:3000/webhooks/quickbutik');
  });
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Event Types" icon="list" href="/webhooks/events">
    Explore all available webhook events and their data
  </Card>

  <Card title="Setup Guide" icon="gear" href="/webhooks/setup">
    Detailed webhook configuration and troubleshooting
  </Card>
</CardGroup>
